This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch 1.X
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git
The following commit(s) were added to refs/heads/1.X by this push:
new cd7afd96 Sort members
cd7afd96 is described below
commit cd7afd96e0f98f83db61b01483319d911dee6d54
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Jul 30 07:06:28 2026 -0400
Sort members
---
.../beanutils/DynaPropertySerializationTest.java | 450 ++++++++++-----------
1 file changed, 225 insertions(+), 225 deletions(-)
diff --git
a/src/test/java/org/apache/commons/beanutils/DynaPropertySerializationTest.java
b/src/test/java/org/apache/commons/beanutils/DynaPropertySerializationTest.java
index a8dff23a..214ac700 100644
---
a/src/test/java/org/apache/commons/beanutils/DynaPropertySerializationTest.java
+++
b/src/test/java/org/apache/commons/beanutils/DynaPropertySerializationTest.java
@@ -51,13 +51,17 @@ import org.junit.jupiter.api.Test;
class DynaPropertySerializationTest {
/**
- * Serializes {@code prop} to a byte array.
+ * Assert that a round-tripped {@link DynaProperty} is equal to the
original and is a distinct object.
*/
- private static byte[] serialize(final DynaProperty prop) throws
IOException {
- try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
- oos.writeObject(prop);
- return bos.toByteArray();
- }
+ private static void assertRoundTrip(final DynaProperty original) throws
IOException, ClassNotFoundException {
+ final DynaProperty copy = roundTrip(original);
+ assertNotNull(copy);
+ assertNotSame(original, copy, "Deserialized instance must be a new
object");
+ assertEquals(original.getName(), copy.getName(), "name must be
preserved");
+ assertEquals(original.getType(), copy.getType(), "type must be
preserved");
+ assertEquals(original.getContentType(), copy.getContentType(),
"contentType must be preserved");
+ assertEquals(original, copy, "equals() must hold after round-trip");
+ assertEquals(original.hashCode(), copy.hashCode(), "hashCode() must
hold after round-trip");
}
/**
@@ -69,6 +73,21 @@ class DynaPropertySerializationTest {
}
}
+ /**
+ * Returns the byte-offset of the first occurrence of {@code needle}
inside {@code haystack}, or {@code -1} if not found.
+ */
+ private static int findSequence(final byte[] haystack, final byte[]
needle) {
+ outer: for (int i = 0; i <= haystack.length - needle.length; i++) {
+ for (int j = 0; j < needle.length; j++) {
+ if (haystack[i + j] != needle[j]) {
+ continue outer;
+ }
+ }
+ return i;
+ }
+ return -1;
+ }
+
/**
* Full round-trip: serialize then deserialize and return the
reconstructed instance.
*/
@@ -77,38 +96,116 @@ class DynaPropertySerializationTest {
}
/**
- * Assert that a round-tripped {@link DynaProperty} is equal to the
original and is a distinct object.
+ * Serializes {@code prop} to a byte array.
*/
- private static void assertRoundTrip(final DynaProperty original) throws
IOException, ClassNotFoundException {
- final DynaProperty copy = roundTrip(original);
- assertNotNull(copy);
- assertNotSame(original, copy, "Deserialized instance must be a new
object");
- assertEquals(original.getName(), copy.getName(), "name must be
preserved");
- assertEquals(original.getType(), copy.getType(), "type must be
preserved");
- assertEquals(original.getContentType(), copy.getContentType(),
"contentType must be preserved");
- assertEquals(original, copy, "equals() must hold after round-trip");
- assertEquals(original.hashCode(), copy.hashCode(), "hashCode() must
hold after round-trip");
+ private static byte[] serialize(final DynaProperty prop) throws
IOException {
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(prop);
+ return bos.toByteArray();
+ }
}
@Test
- void testRoundTripNameOnly() throws Exception {
- // DynaProperty(String) uses Object.class as the type
- assertRoundTrip(new DynaProperty("nameOnly"));
+ void testContentTypeIsNullForSimpleProperty() throws Exception {
+ final DynaProperty copy = roundTrip(new DynaProperty("simple",
String.class));
+ assertNull(copy.getContentType(), "Simple (non-indexed, non-mapped)
property must have null contentType");
}
@Test
- void testRoundTripStringType() throws Exception {
- assertRoundTrip(new DynaProperty("strProp", String.class));
+ void testIsIndexedPreservedAfterRoundTrip() throws Exception {
+ final DynaProperty indexed = new DynaProperty("idx", List.class,
String.class);
+ final DynaProperty copy = roundTrip(indexed);
+ assertEquals(indexed.isIndexed(), copy.isIndexed());
}
@Test
- void testRoundTripIntegerWrapperType() throws Exception {
- assertRoundTrip(new DynaProperty("integerProp", Integer.class));
+ void testIsMappedPreservedAfterRoundTrip() throws Exception {
+ final DynaProperty mapped = new DynaProperty("map", Map.class,
Integer.class);
+ final DynaProperty copy = roundTrip(mapped);
+ assertEquals(mapped.isMapped(), copy.isMapped());
}
@Test
- void testRoundTripObjectType() throws Exception {
- assertRoundTrip(new DynaProperty("objProp", Object.class));
+ void testMultipleSerializationsProduceSameResult() throws Exception {
+ final DynaProperty prop = new DynaProperty("multi", Integer.TYPE);
+ final DynaProperty copy1 = roundTrip(prop);
+ final DynaProperty copy2 = roundTrip(prop);
+ assertEquals(copy1, copy2);
+ assertEquals(copy1.hashCode(), copy2.hashCode());
+ }
+
+ @Test
+ void testNameIsPreservedAfterRoundTrip() throws Exception {
+ final String uniqueName = "uniquePropertyName_42";
+ final DynaProperty copy = roundTrip(new DynaProperty(uniqueName,
String.class));
+ assertEquals(uniqueName, copy.getName());
+ }
+
+ /**
+ * Variant of the corruption test using a <em>mapped</em> property so that
the second {@code readAnyClass} call (for contentType) is the one that receives
+ * the bad constant.
+ */
+ @Test
+ void
testReadObjectThrowsStreamCorruptedExceptionForInvalidContentTypePrimitiveConstant()
throws Exception {
+ // serialize Map<String, Boolean> vs Map<String, Byte> – the type
(Map.class)
+ // is written as a non-primitive class object; the contentType is
written as a
+ // primitive-type constant.
+ final byte[] boolContentBytes = serialize(new
DynaProperty("corruptContent", Map.class, Boolean.TYPE));
+ final byte[] byteContentBytes = serialize(new
DynaProperty("corruptContent", Map.class, Byte.TYPE));
+ int diffIndex = -1;
+ for (int i = 0; i < boolContentBytes.length; i++) {
+ if (boolContentBytes[i] != byteContentBytes[i]) {
+ diffIndex = i;
+ break;
+ }
+ }
+ assertNotNull(Integer.valueOf(diffIndex), "Streams must differ at the
contentType primitive-type int");
+ final byte[] corrupted = boolContentBytes.clone();
+ corrupted[diffIndex - 3] = 0x00;
+ corrupted[diffIndex - 2] = 0x00;
+ corrupted[diffIndex - 1] = 0x00;
+ corrupted[diffIndex] = (byte) 99;
+ final IOException thrown = assertThrows(IOException.class, () ->
deserialize(corrupted),
+ "Expected StreamCorruptedException for unrecognised primitive
contentType constant");
+ assertInstanceOf(StreamCorruptedException.class, thrown, "Root cause
must be StreamCorruptedException");
+ }
+
+ /**
+ * Verifies that {@link DynaProperty#readObject(ObjectInputStream)} throws
a {@link StreamCorruptedException} when the stream contains an unrecognised
+ * primitive-type constant (i.e. an integer that is not in the range 1–8).
+ * <p>
+ * Strategy: serialize two {@code DynaProperty} instances whose types
differ only in the primitive-type constant written by {@code writeAnyClass}
+ * (BOOLEAN_TYPE=1 vs BYTE_TYPE=2). Find the first differing byte (the
last byte of the 4-byte int), then replace those 4 bytes with the invalid
constant 99
+ * before attempting deserialisation.
+ * </p>
+ */
+ @Test
+ void
testReadObjectThrowsStreamCorruptedExceptionForInvalidPrimitiveTypeConstant()
throws Exception {
+ // serialize a DynaProperty whose type is stored as BOOLEAN_TYPE (int
1)
+ final byte[] booleanBytes = serialize(new DynaProperty("corrupt",
Boolean.TYPE));
+ // serialize the same name/class but with BYTE_TYPE (int 2) to locate
the int
+ final byte[] byteBytes = serialize(new DynaProperty("corrupt",
Byte.TYPE));
+ // The two streams should be identical except for the last byte of the
4-byte
+ // primitive-type int (0x01 vs 0x02). Find the first (and only)
differing byte.
+ int diffIndex = -1;
+ for (int i = 0; i < booleanBytes.length; i++) {
+ if (booleanBytes[i] != byteBytes[i]) {
+ diffIndex = i;
+ break;
+ }
+ }
+ assertNotNull(Integer.valueOf(diffIndex), "Streams must differ at the
primitive-type int");
+ // The 4-byte int starts 3 bytes before the differing byte
(big-endian).
+ // Overwrite all 4 bytes with the invalid constant 99 (0x00_00_00_63).
+ final byte[] corrupted = booleanBytes.clone();
+ corrupted[diffIndex - 3] = 0x00;
+ corrupted[diffIndex - 2] = 0x00;
+ corrupted[diffIndex - 1] = 0x00;
+ corrupted[diffIndex] = (byte) 99;
+ // Deserialising the corrupted stream must throw
StreamCorruptedException.
+ final IOException thrown = assertThrows(IOException.class, () ->
deserialize(corrupted),
+ "Expected StreamCorruptedException for unrecognised
primitive-type constant");
+ assertInstanceOf(StreamCorruptedException.class, thrown, "Root cause
must be StreamCorruptedException");
}
@Test
@@ -127,44 +224,41 @@ class DynaPropertySerializationTest {
}
@Test
- void testRoundTripDoublePrimitiveType() throws Exception {
- assertRoundTrip(new DynaProperty("doubleProp", Double.TYPE));
+ void testRoundTripConcreteListSubtype() throws Exception {
+ // ArrayList also satisfies isIndexed()
+ assertRoundTrip(new DynaProperty("arrayListProp", ArrayList.class,
String.class));
}
@Test
- void testRoundTripFloatPrimitiveType() throws Exception {
- assertRoundTrip(new DynaProperty("floatProp", Float.TYPE));
+ void testRoundTripConcreteMapSubtype() throws Exception {
+ // HashMap also satisfies isMapped()
+ assertRoundTrip(new DynaProperty("hashMapProp", HashMap.class,
Double.TYPE));
}
@Test
- void testRoundTripIntPrimitiveType() throws Exception {
- assertRoundTrip(new DynaProperty("intProp", Integer.TYPE));
+ void testRoundTripDoublePrimitiveType() throws Exception {
+ assertRoundTrip(new DynaProperty("doubleProp", Double.TYPE));
}
@Test
- void testRoundTripLongPrimitiveType() throws Exception {
- assertRoundTrip(new DynaProperty("longProp", Long.TYPE));
+ void testRoundTripFloatPrimitiveType() throws Exception {
+ assertRoundTrip(new DynaProperty("floatProp", Float.TYPE));
}
@Test
- void testRoundTripShortPrimitiveType() throws Exception {
- assertRoundTrip(new DynaProperty("shortProp", Short.TYPE));
+ void testRoundTripIntegerWrapperType() throws Exception {
+ assertRoundTrip(new DynaProperty("integerProp", Integer.class));
}
@Test
- void testRoundTripObjectArrayType() throws Exception {
- // String[] – contentType is String.class (non-primitive)
- assertRoundTrip(new DynaProperty("strArrayProp", String[].class));
+ void testRoundTripIntPrimitiveType() throws Exception {
+ assertRoundTrip(new DynaProperty("intProp", Integer.TYPE));
}
@Test
- void testRoundTripPrimitiveArrayType() throws Exception {
- // int[] – constructor sets contentType = Integer.TYPE (primitive)
- final DynaProperty prop = new DynaProperty("intArrayProp",
int[].class);
- assertRoundTrip(prop);
- // Verify contentType is correctly restored as the primitive type
- final DynaProperty copy = roundTrip(prop);
- assertEquals(Integer.TYPE, copy.getContentType());
+ void testRoundTripListTypeWithExplicitContentType() throws Exception {
+ // List<String> – explicit contentType is an object class
+ assertRoundTrip(new DynaProperty("typedListProp", List.class,
String.class));
}
@Test
@@ -173,12 +267,6 @@ class DynaPropertySerializationTest {
assertRoundTrip(new DynaProperty("listProp", List.class));
}
- @Test
- void testRoundTripListTypeWithExplicitContentType() throws Exception {
- // List<String> – explicit contentType is an object class
- assertRoundTrip(new DynaProperty("typedListProp", List.class,
String.class));
- }
-
@Test
void testRoundTripListTypeWithPrimitiveContentType() throws Exception {
// List<int> – explicit contentType is a primitive type
@@ -186,9 +274,8 @@ class DynaPropertySerializationTest {
}
@Test
- void testRoundTripConcreteListSubtype() throws Exception {
- // ArrayList also satisfies isIndexed()
- assertRoundTrip(new DynaProperty("arrayListProp", ArrayList.class,
String.class));
+ void testRoundTripLongPrimitiveType() throws Exception {
+ assertRoundTrip(new DynaProperty("longProp", Long.TYPE));
}
@Test
@@ -210,61 +297,40 @@ class DynaPropertySerializationTest {
}
@Test
- void testRoundTripConcreteMapSubtype() throws Exception {
- // HashMap also satisfies isMapped()
- assertRoundTrip(new DynaProperty("hashMapProp", HashMap.class,
Double.TYPE));
- }
-
- @Test
- void testNameIsPreservedAfterRoundTrip() throws Exception {
- final String uniqueName = "uniquePropertyName_42";
- final DynaProperty copy = roundTrip(new DynaProperty(uniqueName,
String.class));
- assertEquals(uniqueName, copy.getName());
- }
-
- @Test
- void testTypeIsPreservedAfterRoundTripForEveryPrimitive() throws Exception
{
- final Class<?>[] primitives = { Boolean.TYPE, Byte.TYPE,
Character.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE, Short.TYPE };
- for (final Class<?> primitive : primitives) {
- final DynaProperty copy = roundTrip(new DynaProperty("p",
primitive));
- assertEquals(primitive, copy.getType(), () -> "type must be
preserved for primitive " + primitive.getName());
- }
+ void testRoundTripNameOnly() throws Exception {
+ // DynaProperty(String) uses Object.class as the type
+ assertRoundTrip(new DynaProperty("nameOnly"));
}
@Test
- void testContentTypeIsNullForSimpleProperty() throws Exception {
- final DynaProperty copy = roundTrip(new DynaProperty("simple",
String.class));
- assertNull(copy.getContentType(), "Simple (non-indexed, non-mapped)
property must have null contentType");
+ void testRoundTripObjectArrayType() throws Exception {
+ // String[] – contentType is String.class (non-primitive)
+ assertRoundTrip(new DynaProperty("strArrayProp", String[].class));
}
@Test
- void testIsIndexedPreservedAfterRoundTrip() throws Exception {
- final DynaProperty indexed = new DynaProperty("idx", List.class,
String.class);
- final DynaProperty copy = roundTrip(indexed);
- assertEquals(indexed.isIndexed(), copy.isIndexed());
+ void testRoundTripObjectType() throws Exception {
+ assertRoundTrip(new DynaProperty("objProp", Object.class));
}
@Test
- void testIsMappedPreservedAfterRoundTrip() throws Exception {
- final DynaProperty mapped = new DynaProperty("map", Map.class,
Integer.class);
- final DynaProperty copy = roundTrip(mapped);
- assertEquals(mapped.isMapped(), copy.isMapped());
+ void testRoundTripPrimitiveArrayType() throws Exception {
+ // int[] – constructor sets contentType = Integer.TYPE (primitive)
+ final DynaProperty prop = new DynaProperty("intArrayProp",
int[].class);
+ assertRoundTrip(prop);
+ // Verify contentType is correctly restored as the primitive type
+ final DynaProperty copy = roundTrip(prop);
+ assertEquals(Integer.TYPE, copy.getContentType());
}
@Test
- void testToStringAfterRoundTrip() throws Exception {
- final DynaProperty original = new DynaProperty("myProp", List.class,
String.class);
- final DynaProperty copy = roundTrip(original);
- assertEquals(original.toString(), copy.toString());
+ void testRoundTripShortPrimitiveType() throws Exception {
+ assertRoundTrip(new DynaProperty("shortProp", Short.TYPE));
}
@Test
- void testMultipleSerializationsProduceSameResult() throws Exception {
- final DynaProperty prop = new DynaProperty("multi", Integer.TYPE);
- final DynaProperty copy1 = roundTrip(prop);
- final DynaProperty copy2 = roundTrip(prop);
- assertEquals(copy1, copy2);
- assertEquals(copy1.hashCode(), copy2.hashCode());
+ void testRoundTripStringType() throws Exception {
+ assertRoundTrip(new DynaProperty("strProp", String.class));
}
@Test
@@ -276,129 +342,59 @@ class DynaPropertySerializationTest {
assertEquals(original, copy2);
}
- /**
- * Verifies that {@link DynaProperty#readObject(ObjectInputStream)} throws
a {@link StreamCorruptedException} when the stream contains an unrecognised
- * primitive-type constant (i.e. an integer that is not in the range 1–8).
- * <p>
- * Strategy: serialize two {@code DynaProperty} instances whose types
differ only in the primitive-type constant written by {@code writeAnyClass}
- * (BOOLEAN_TYPE=1 vs BYTE_TYPE=2). Find the first differing byte (the
last byte of the 4-byte int), then replace those 4 bytes with the invalid
constant 99
- * before attempting deserialisation.
- * </p>
- */
@Test
- void
testReadObjectThrowsStreamCorruptedExceptionForInvalidPrimitiveTypeConstant()
throws Exception {
- // serialize a DynaProperty whose type is stored as BOOLEAN_TYPE (int
1)
- final byte[] booleanBytes = serialize(new DynaProperty("corrupt",
Boolean.TYPE));
- // serialize the same name/class but with BYTE_TYPE (int 2) to locate
the int
- final byte[] byteBytes = serialize(new DynaProperty("corrupt",
Byte.TYPE));
- // The two streams should be identical except for the last byte of the
4-byte
- // primitive-type int (0x01 vs 0x02). Find the first (and only)
differing byte.
- int diffIndex = -1;
- for (int i = 0; i < booleanBytes.length; i++) {
- if (booleanBytes[i] != byteBytes[i]) {
- diffIndex = i;
- break;
- }
- }
- assertNotNull(Integer.valueOf(diffIndex), "Streams must differ at the
primitive-type int");
- // The 4-byte int starts 3 bytes before the differing byte
(big-endian).
- // Overwrite all 4 bytes with the invalid constant 99 (0x00_00_00_63).
- final byte[] corrupted = booleanBytes.clone();
- corrupted[diffIndex - 3] = 0x00;
- corrupted[diffIndex - 2] = 0x00;
- corrupted[diffIndex - 1] = 0x00;
- corrupted[diffIndex] = (byte) 99;
- // Deserialising the corrupted stream must throw
StreamCorruptedException.
- final IOException thrown = assertThrows(IOException.class, () ->
deserialize(corrupted),
- "Expected StreamCorruptedException for unrecognised
primitive-type constant");
- assertInstanceOf(StreamCorruptedException.class, thrown, "Root cause
must be StreamCorruptedException");
+ void testToStringAfterRoundTrip() throws Exception {
+ final DynaProperty original = new DynaProperty("myProp", List.class,
String.class);
+ final DynaProperty copy = roundTrip(original);
+ assertEquals(original.toString(), copy.toString());
}
- /**
- * Variant of the corruption test using a <em>mapped</em> property so that
the second {@code readAnyClass} call (for contentType) is the one that receives
- * the bad constant.
- */
@Test
- void
testReadObjectThrowsStreamCorruptedExceptionForInvalidContentTypePrimitiveConstant()
throws Exception {
- // serialize Map<String, Boolean> vs Map<String, Byte> – the type
(Map.class)
- // is written as a non-primitive class object; the contentType is
written as a
- // primitive-type constant.
- final byte[] boolContentBytes = serialize(new
DynaProperty("corruptContent", Map.class, Boolean.TYPE));
- final byte[] byteContentBytes = serialize(new
DynaProperty("corruptContent", Map.class, Byte.TYPE));
- int diffIndex = -1;
- for (int i = 0; i < boolContentBytes.length; i++) {
- if (boolContentBytes[i] != byteContentBytes[i]) {
- diffIndex = i;
- break;
- }
- }
- assertNotNull(Integer.valueOf(diffIndex), "Streams must differ at the
contentType primitive-type int");
- final byte[] corrupted = boolContentBytes.clone();
- corrupted[diffIndex - 3] = 0x00;
- corrupted[diffIndex - 2] = 0x00;
- corrupted[diffIndex - 1] = 0x00;
- corrupted[diffIndex] = (byte) 99;
- final IOException thrown = assertThrows(IOException.class, () ->
deserialize(corrupted),
- "Expected StreamCorruptedException for unrecognised primitive
contentType constant");
- assertInstanceOf(StreamCorruptedException.class, thrown, "Root cause
must be StreamCorruptedException");
- }
-
- /**
- * Returns the byte-offset of the first occurrence of {@code needle}
inside {@code haystack}, or {@code -1} if not found.
- */
- private static int findSequence(final byte[] haystack, final byte[]
needle) {
- outer: for (int i = 0; i <= haystack.length - needle.length; i++) {
- for (int j = 0; j < needle.length; j++) {
- if (haystack[i + j] != needle[j]) {
- continue outer;
- }
- }
- return i;
+ void testTypeIsPreservedAfterRoundTripForEveryPrimitive() throws Exception
{
+ final Class<?>[] primitives = { Boolean.TYPE, Byte.TYPE,
Character.TYPE, Double.TYPE, Float.TYPE, Integer.TYPE, Long.TYPE, Short.TYPE };
+ for (final Class<?> primitive : primitives) {
+ final DynaProperty copy = roundTrip(new DynaProperty("p",
primitive));
+ assertEquals(primitive, copy.getType(), () -> "type must be
preserved for primitive " + primitive.getName());
}
- return -1;
}
/**
- * Proves that {@link DynaProperty#writeObject(ObjectOutputStream)} calls
{@code writeAnyClass} (which encodes the {@code type} field) <em>before</em>
- * {@code defaultWriteObject} (which encodes the {@code name} field) for
<strong>primitive types</strong>.
+ * Proves that for <strong>indexed and mapped properties</strong> the
second {@code writeAnyClass} call (for {@code contentType}) also appears in the
byte
+ * stream <em>before</em> the {@code name} field written by {@code
defaultWriteObject}.
* <p>
- * Strategy: serialise two {@link DynaProperty} instances that have an
identical {@code name} but different primitive types. Because the name is
identical,
- * both byte streams are the same from the class-descriptor through to the
end of the default-field data. The streams diverge only at the
- * {@code writeAnyClass} output (the primitive-type integer constant). If
that divergence point is located <em>before</em> the name bytes in the stream,
it
- * proves that {@code writeAnyClass} is called first.
+ * Two mapped properties share the same name and the same {@code
Map.class} type but differ in their {@code contentType} ({@code Boolean.TYPE} vs
+ * {@code Byte.TYPE}). Because the type ({@code Map.class}) is identical,
the streams are the same through the first {@code writeAnyClass} call. They
+ * diverge at the second {@code writeAnyClass} call (contentType
constant), which must still precede the name.
* </p>
*/
@Test
- void testWireFormatPrimitiveTypeDataPrecedesNameField() throws Exception {
- final String sharedName = "sharedPrimitiveName";
- // Boolean.TYPE encodes as writeBoolean(true) +
writeInt(BOOLEAN_TYPE=1).
- // Byte.TYPE encodes as writeBoolean(true) + writeInt(BYTE_TYPE=2).
- // The two streams are byte-for-byte identical except at the
primitive-type int.
- final byte[] boolBytes = serialize(new DynaProperty(sharedName,
Boolean.TYPE));
- final byte[] byteBytes = serialize(new DynaProperty(sharedName,
Byte.TYPE));
- // Locate the shared name in the boolean-type stream.
+ void testWireFormatContentTypeDataPrecedesNameFieldForMappedProperty()
throws Exception {
+ final String sharedName = "sharedMappedName";
+ // Both use Map.class as the type (identical first writeAnyClass
output).
+ // They differ only in contentType: Boolean.TYPE (constant 1) vs
Byte.TYPE (constant 2).
+ final byte[] boolContentBytes = serialize(new DynaProperty(sharedName,
Map.class, Boolean.TYPE));
+ final byte[] byteContentBytes = serialize(new DynaProperty(sharedName,
Map.class, Byte.TYPE));
final byte[] nameBytes = sharedName.getBytes(StandardCharsets.UTF_8);
- final int namePosition = findSequence(boolBytes, nameBytes);
- assertTrue(namePosition > 0, "Property name must be present in the
serialized stream");
- // The name must occupy the same position in both streams (identical
name, identical class).
- assertEquals(namePosition, findSequence(byteBytes, nameBytes), "Name
must be at the same byte position in both streams");
- // Find the first byte where the two streams diverge: this is inside
the
- // writeAnyClass output (the primitive-type integer constant differs:
1 vs 2).
+ final int namePosition = findSequence(boolContentBytes, nameBytes);
+ assertTrue(namePosition > 0, "Name must be present in the serialized
stream");
+ assertEquals(namePosition, findSequence(byteContentBytes, nameBytes),
"Name must be at the same byte position in both streams");
+ // The streams are identical up to (and including) the type encoding
of Map.class.
+ // They diverge at the contentType constant written by the second
writeAnyClass call.
int firstDiff = -1;
- for (int i = 0; i < boolBytes.length; i++) {
- if (boolBytes[i] != byteBytes[i]) {
+ for (int i = 0; i < boolContentBytes.length; i++) {
+ if (boolContentBytes[i] != byteContentBytes[i]) {
firstDiff = i;
break;
}
}
- assertTrue(firstDiff >= 0, "Streams must diverge at the primitive-type
constant");
- // CRITICAL assertion: the divergence point (type data from
writeAnyClass) must
- // come BEFORE the name field (from defaultWriteObject).
- assertTrue(firstDiff < namePosition, "writeAnyClass must be invoked
before defaultWriteObject: " + "type-data divergence at byte " + firstDiff
- + " must precede name field at byte " + namePosition);
- // Sanity-check: both properties still round-trip correctly.
- assertRoundTrip(new DynaProperty(sharedName, Boolean.TYPE));
- assertRoundTrip(new DynaProperty(sharedName, Byte.TYPE));
+ assertTrue(firstDiff >= 0, "Streams must diverge at the contentType
primitive-type constant");
+ // The second writeAnyClass output (contentType) must still precede
the name
+ // (defaultWriteObject), confirming that both writeAnyClass calls
happen before
+ // defaultWriteObject is invoked.
+ assertTrue(firstDiff < namePosition, "The second writeAnyClass call
(contentType) must precede defaultWriteObject: "
+ + "content-type divergence at byte " + firstDiff + " must
precede name at byte " + namePosition);
+ assertRoundTrip(new DynaProperty(sharedName, Map.class, Boolean.TYPE));
+ assertRoundTrip(new DynaProperty(sharedName, Map.class, Byte.TYPE));
}
/**
@@ -444,41 +440,45 @@ class DynaPropertySerializationTest {
}
/**
- * Proves that for <strong>indexed and mapped properties</strong> the
second {@code writeAnyClass} call (for {@code contentType}) also appears in the
byte
- * stream <em>before</em> the {@code name} field written by {@code
defaultWriteObject}.
+ * Proves that {@link DynaProperty#writeObject(ObjectOutputStream)} calls
{@code writeAnyClass} (which encodes the {@code type} field) <em>before</em>
+ * {@code defaultWriteObject} (which encodes the {@code name} field) for
<strong>primitive types</strong>.
* <p>
- * Two mapped properties share the same name and the same {@code
Map.class} type but differ in their {@code contentType} ({@code Boolean.TYPE} vs
- * {@code Byte.TYPE}). Because the type ({@code Map.class}) is identical,
the streams are the same through the first {@code writeAnyClass} call. They
- * diverge at the second {@code writeAnyClass} call (contentType
constant), which must still precede the name.
+ * Strategy: serialise two {@link DynaProperty} instances that have an
identical {@code name} but different primitive types. Because the name is
identical,
+ * both byte streams are the same from the class-descriptor through to the
end of the default-field data. The streams diverge only at the
+ * {@code writeAnyClass} output (the primitive-type integer constant). If
that divergence point is located <em>before</em> the name bytes in the stream,
it
+ * proves that {@code writeAnyClass} is called first.
* </p>
*/
@Test
- void testWireFormatContentTypeDataPrecedesNameFieldForMappedProperty()
throws Exception {
- final String sharedName = "sharedMappedName";
- // Both use Map.class as the type (identical first writeAnyClass
output).
- // They differ only in contentType: Boolean.TYPE (constant 1) vs
Byte.TYPE (constant 2).
- final byte[] boolContentBytes = serialize(new DynaProperty(sharedName,
Map.class, Boolean.TYPE));
- final byte[] byteContentBytes = serialize(new DynaProperty(sharedName,
Map.class, Byte.TYPE));
+ void testWireFormatPrimitiveTypeDataPrecedesNameField() throws Exception {
+ final String sharedName = "sharedPrimitiveName";
+ // Boolean.TYPE encodes as writeBoolean(true) +
writeInt(BOOLEAN_TYPE=1).
+ // Byte.TYPE encodes as writeBoolean(true) + writeInt(BYTE_TYPE=2).
+ // The two streams are byte-for-byte identical except at the
primitive-type int.
+ final byte[] boolBytes = serialize(new DynaProperty(sharedName,
Boolean.TYPE));
+ final byte[] byteBytes = serialize(new DynaProperty(sharedName,
Byte.TYPE));
+ // Locate the shared name in the boolean-type stream.
final byte[] nameBytes = sharedName.getBytes(StandardCharsets.UTF_8);
- final int namePosition = findSequence(boolContentBytes, nameBytes);
- assertTrue(namePosition > 0, "Name must be present in the serialized
stream");
- assertEquals(namePosition, findSequence(byteContentBytes, nameBytes),
"Name must be at the same byte position in both streams");
- // The streams are identical up to (and including) the type encoding
of Map.class.
- // They diverge at the contentType constant written by the second
writeAnyClass call.
+ final int namePosition = findSequence(boolBytes, nameBytes);
+ assertTrue(namePosition > 0, "Property name must be present in the
serialized stream");
+ // The name must occupy the same position in both streams (identical
name, identical class).
+ assertEquals(namePosition, findSequence(byteBytes, nameBytes), "Name
must be at the same byte position in both streams");
+ // Find the first byte where the two streams diverge: this is inside
the
+ // writeAnyClass output (the primitive-type integer constant differs:
1 vs 2).
int firstDiff = -1;
- for (int i = 0; i < boolContentBytes.length; i++) {
- if (boolContentBytes[i] != byteContentBytes[i]) {
+ for (int i = 0; i < boolBytes.length; i++) {
+ if (boolBytes[i] != byteBytes[i]) {
firstDiff = i;
break;
}
}
- assertTrue(firstDiff >= 0, "Streams must diverge at the contentType
primitive-type constant");
- // The second writeAnyClass output (contentType) must still precede
the name
- // (defaultWriteObject), confirming that both writeAnyClass calls
happen before
- // defaultWriteObject is invoked.
- assertTrue(firstDiff < namePosition, "The second writeAnyClass call
(contentType) must precede defaultWriteObject: "
- + "content-type divergence at byte " + firstDiff + " must
precede name at byte " + namePosition);
- assertRoundTrip(new DynaProperty(sharedName, Map.class, Boolean.TYPE));
- assertRoundTrip(new DynaProperty(sharedName, Map.class, Byte.TYPE));
+ assertTrue(firstDiff >= 0, "Streams must diverge at the primitive-type
constant");
+ // CRITICAL assertion: the divergence point (type data from
writeAnyClass) must
+ // come BEFORE the name field (from defaultWriteObject).
+ assertTrue(firstDiff < namePosition, "writeAnyClass must be invoked
before defaultWriteObject: " + "type-data divergence at byte " + firstDiff
+ + " must precede name field at byte " + namePosition);
+ // Sanity-check: both properties still round-trip correctly.
+ assertRoundTrip(new DynaProperty(sharedName, Boolean.TYPE));
+ assertRoundTrip(new DynaProperty(sharedName, Byte.TYPE));
}
}