summaryrefslogtreecommitdiff
path: root/lib/java/test/org
diff options
context:
space:
mode:
authorkpandit <kpandit@pinterest.com>2021-11-20 00:56:17 +0100
committerJens Geyer <jensg@apache.org>2021-11-20 00:57:57 +0100
commit5a9d139be4ef1a790da4c6f25377b8ab6573a325 (patch)
tree0377e631e8faada4e4c7cb1db3e47af89518a650 /lib/java/test/org
parent2c0927826d1e7f7e902f29a925e22058f949f535 (diff)
downloadthrift-5a9d139be4ef1a790da4c6f25377b8ab6573a325.tar.gz
THRIFT-5443: add support for partial Thrift deserialization
Client: java Patch: Bhalchandra Pandit This closes #2439
Diffstat (limited to 'lib/java/test/org')
-rw-r--r--lib/java/test/org/apache/thrift/TestPartialThriftDeserializer.java580
-rw-r--r--lib/java/test/org/apache/thrift/partial/EnumCacheTest.java114
-rw-r--r--lib/java/test/org/apache/thrift/partial/ExceptionAsserts.java92
-rw-r--r--lib/java/test/org/apache/thrift/partial/PartialThriftComparerTest.java75
-rw-r--r--lib/java/test/org/apache/thrift/partial/PartialThriftTestData.java311
-rw-r--r--lib/java/test/org/apache/thrift/partial/TFieldDataTest.java51
-rw-r--r--lib/java/test/org/apache/thrift/partial/TestData.java57
-rw-r--r--lib/java/test/org/apache/thrift/partial/ThriftFieldTest.java149
-rw-r--r--lib/java/test/org/apache/thrift/partial/ThriftMetadataTest.java294
-rw-r--r--lib/java/test/org/apache/thrift/partial/ThriftSerDe.java71
-rw-r--r--lib/java/test/org/apache/thrift/partial/ThriftStructProcessorTest.java315
-rw-r--r--lib/java/test/org/apache/thrift/partial/ValidateTest.java325
12 files changed, 2434 insertions, 0 deletions
diff --git a/lib/java/test/org/apache/thrift/TestPartialThriftDeserializer.java b/lib/java/test/org/apache/thrift/TestPartialThriftDeserializer.java
new file mode 100644
index 000000000..c0c7b892d
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/TestPartialThriftDeserializer.java
@@ -0,0 +1,580 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.*;
+
+import org.apache.thrift.partial.TestStruct;
+import org.apache.thrift.partial.ThriftField;
+import org.apache.thrift.partial.TstEnum;
+import org.apache.thrift.partial.ExceptionAsserts;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class TestPartialThriftDeserializer {
+
+ private ThriftSerDe serde = new ThriftSerDe();
+ private TBinaryProtocol.Factory binaryProtocolFactory = new TBinaryProtocol.Factory();
+ private TCompactProtocol.Factory compactProtocolFactory = new TCompactProtocol.Factory();
+
+ private PartialThriftTestData testData = new PartialThriftTestData();
+
+ public TestPartialThriftDeserializer() throws TException {
+ }
+
+ @Test
+ public void testArgChecks() throws TException {
+ // Should not throw.
+ List<String> fieldNames = Arrays.asList("i32Field");
+ new TDeserializer(TestStruct.class, fieldNames, binaryProtocolFactory);
+
+ // Verify it throws correctly.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'thriftClass' must not be null",
+ () -> new TDeserializer(null, fieldNames, binaryProtocolFactory));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'fieldNames' must not be null",
+ () -> new TDeserializer(TestStruct.class, null, binaryProtocolFactory));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'processor' must not be null",
+ () -> new TDeserializer(TestStruct.class, fieldNames, null, binaryProtocolFactory));
+ }
+
+ /**
+ * This test does not use partial deserialization. It is used to establish correctness
+ * of full serialization used in the other tests.
+ */
+ @Test
+ public void testRoundTripFull() throws TException {
+ TestStruct ts1 = testData.createTestStruct(1, 2);
+
+ byte[] bytesBinary = serde.serializeBinary(ts1);
+ byte[] bytesCompact = serde.serializeCompact(ts1);
+
+ TestStruct ts2 = serde.deserializeBinary(bytesBinary, TestStruct.class);
+ assertEquals(ts1, ts2);
+
+ ts2 = serde.deserializeCompact(bytesCompact, TestStruct.class);
+ assertEquals(ts1, ts2);
+ }
+
+ @Test
+ public void testPartialSimpleField() throws TException, IOException {
+ TestStruct ts1 = testData.createTestStruct(1, 1);
+ assertTrue(ts1.isSetI16Field());
+ assertTrue(ts1.isSetI32Field());
+
+ byte[] bytesBinary = serde.serializeBinary(ts1);
+ byte[] bytesCompact = serde.serializeCompact(ts1);
+
+ List<String> fieldNames = Arrays.asList("i32Field");
+
+ TDeserializer partialBinaryDeserializer =
+ new TDeserializer(TestStruct.class, fieldNames, binaryProtocolFactory);
+ TDeserializer partialCompactDeserializer =
+ new TDeserializer(TestStruct.class, fieldNames, compactProtocolFactory);
+
+ PartialThriftComparer comparer =
+ new PartialThriftComparer(partialBinaryDeserializer.getMetadata());
+
+ StringBuilder sb = new StringBuilder();
+ TestStruct ts2 = (TestStruct) partialBinaryDeserializer.partialDeserializeObject(bytesBinary);
+ validatePartialSimpleField(ts1, ts2);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+
+ ts2 = (TestStruct) partialCompactDeserializer.partialDeserializeObject(bytesCompact);
+ validatePartialSimpleField(ts1, ts2);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+ }
+
+ private void validatePartialSimpleField(TestStruct ts1, TestStruct ts2) {
+ assertTrue(ts2.toString(), ts2.isSetI32Field());
+ assertEquals(ts1.getI32Field(), ts2.getI32Field());
+ assertFalse(ts2.isSetI16Field());
+ }
+
+ @Test
+ public void testPartialComplex() throws TException {
+ int id = 1;
+ int numItems = 10;
+ TestStruct ts1 = testData.createTestStruct(id, numItems);
+
+ byte[] bytesBinary = serde.serializeBinary(ts1);
+ byte[] bytesCompact = serde.serializeCompact(ts1);
+
+ List<String> fieldNames = Arrays.asList(
+ "byteField",
+ "i16Field",
+ "i32Field",
+ "i64Field",
+ "doubleField",
+ "stringField",
+
+ "enumField",
+ "binaryField",
+
+ // List fields
+ "byteList",
+ "i16List",
+ "i32List",
+ "i64List",
+ "doubleList",
+ "stringList",
+ "enumList",
+ "listList",
+ "setList",
+ "mapList",
+ "structList",
+ "binaryList",
+
+ // Set fields
+ "byteSet",
+ "i16Set",
+ "i32Set",
+ "i64Set",
+ "doubleSet",
+ "stringSet",
+ "enumSet",
+ "listSet",
+ "setSet",
+ "mapSet",
+ "structSet",
+ "binarySet",
+
+ // Map fields
+ "byteMap",
+ "i16Map",
+ "i32Map",
+ "i64Map",
+ "doubleMap",
+ "stringMap",
+ "enumMap",
+ "listMap",
+ "setMap",
+ "mapMap",
+ "structMap",
+ "binaryMap",
+
+ // Struct field
+ "structField"
+ );
+ StringBuilder sb = new StringBuilder();
+ TDeserializer partialBinaryDeserializer =
+ new TDeserializer(TestStruct.class, fieldNames, binaryProtocolFactory);
+ TDeserializer partialCompactDeserializer =
+ new TDeserializer(TestStruct.class, fieldNames, compactProtocolFactory);
+ PartialThriftComparer comparer =
+ new PartialThriftComparer(partialBinaryDeserializer.getMetadata());
+
+ TestStruct ts2 = (TestStruct) partialBinaryDeserializer.partialDeserializeObject(bytesBinary);
+ validatePartialComplex(ts1, ts2, id, numItems);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+
+ ts2 = (TestStruct) partialCompactDeserializer.partialDeserializeObject(bytesCompact);
+ validatePartialComplex(ts1, ts2, id, numItems);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+ }
+
+ private void validatePartialComplex(TestStruct ts1, TestStruct ts2, int id, int numItems) {
+
+ // Validate primitive fields.
+ assertTrue(ts2.toString(), ts2.isSetByteField());
+ assertEquals(ts1.getByteField(), ts2.getByteField());
+
+ assertTrue(ts2.isSetI16Field());
+ assertEquals(ts1.getI16Field(), ts2.getI16Field());
+
+ assertTrue(ts2.isSetI32Field());
+ assertEquals(ts1.getI32Field(), ts2.getI32Field());
+
+ assertTrue(ts2.isSetI64Field());
+ assertEquals(ts1.getI64Field(), ts2.getI64Field());
+
+ assertTrue(ts2.isSetDoubleField());
+ assertEquals(ts1.getDoubleField(), ts2.getDoubleField(), 0.0001);
+
+ assertTrue(ts2.isSetStringField());
+ assertEquals(ts1.getStringField(), ts2.getStringField());
+
+ assertTrue(ts2.isSetEnumField());
+ assertEquals(ts1.getEnumField(), ts2.getEnumField());
+
+ assertTrue(ts2.isSetBinaryField());
+ assertArrayEquals(ts1.getBinaryField(), ts2.getBinaryField());
+
+ // Validate list fields.
+ validateList(ts2.getByteList(), id, numItems);
+ validateList(ts2.getI16List(), id, numItems);
+ validateList(ts2.getI32List(), id, numItems);
+ validateList(ts2.getI64List(), id, numItems);
+ validateList(ts2.getDoubleList(), id, numItems);
+ validateStringList(ts2.getStringList(), id, numItems);
+ validateEnumList(ts2.getEnumList(), id, numItems);
+
+ validateListOfList(ts2.getListList(), id, numItems);
+ validateListOfSet(ts2.getSetList(), id, numItems);
+ validateListOfMap(ts2.getMapList(), id, numItems);
+ validateListOfStruct(ts2.getStructList(), id, numItems);
+ validateListOfBinary(ts2.getBinaryList(), id, numItems);
+
+ // Validate set fields.
+ validateSet(ts2.getByteSet(), Byte.class, numItems);
+ validateSet(ts2.getI16Set(), Short.class, numItems);
+ validateSet(ts2.getI32Set(), Integer.class, numItems);
+ validateSet(ts2.getI64Set(), Long.class, numItems);
+ validateSet(ts2.getDoubleSet(), Double.class, numItems);
+ validateStringSet(ts2.getStringSet(), id, numItems);
+ validateEnumSet(ts2.getEnumSet(), id, numItems);
+
+ validateSetOfList(ts2.getListSet(), id, numItems);
+ validateSetOfSet(ts2.getSetSet(), id, numItems);
+ validateSetOfMap(ts2.getMapSet(), id, numItems);
+ validateSetOfStruct(ts2.getStructSet(), id, numItems);
+ validateSetOfBinary(ts2.getBinarySet(), id, numItems);
+
+ // Validate map fields.
+ validateMap(ts2.getByteMap(), Byte.class, numItems);
+ validateMap(ts2.getI16Map(), Short.class, numItems);
+ validateMap(ts2.getI32Map(), Integer.class, numItems);
+ validateMap(ts2.getI64Map(), Long.class, numItems);
+ validateMap(ts2.getDoubleMap(), Double.class, numItems);
+ validateStringMap(ts2.getStringMap(), id, numItems);
+ validateEnumMap(ts2.getEnumMap(), id, numItems);
+
+ validateMapOfList(ts2.getListMap(), id, numItems);
+ validateMapOfSet(ts2.getSetMap(), id, numItems);
+ validateMapOfMap(ts2.getMapMap(), id, numItems);
+ validateMapOfStruct(ts2.getStructMap(), id, numItems);
+ validateMapOfBinary(ts2.getBinaryMap(), id, numItems);
+
+ // Validate struct field.
+ assertEquals(testData.createSmallStruct(id), ts2.getStructField());
+ }
+
+ private void validateNotNullAndNotEmpty(Collection<?> collection, int numItems) {
+ assertNotNull(collection);
+ assertEquals(numItems, collection.size());
+ }
+
+ // ----------------------------------------------------------------------
+ // List validation helpers.
+
+ private <V extends Number> void validateList(List<V> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ assertEquals(i, list.get(i).longValue());
+ }
+ }
+
+ private void validateStringList(List<String> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+ for (int i = 0; i < numItems; i++) {
+ assertEquals(Integer.valueOf(i), Integer.valueOf(list.get(i)));
+ }
+ }
+
+ private void validateEnumList(List<TstEnum> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+ for (int i = 0; i < numItems; i++) {
+ assertEquals(TstEnum.E_ONE, list.get(i));
+ }
+ }
+
+ private <V extends Number> void validateListOfList(List<List<V>> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ validateList(list.get(i), id, numItems);
+ }
+ }
+
+ private <V extends Number> void validateListOfSet(List<Set<V>> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ Set<V> set = list.get(i);
+ for (int j = 0; j < numItems; j++) {
+ assertTrue(set.contains(j));
+ }
+ }
+ }
+
+ private <V extends Number> void validateListOfMap(
+ List<Map<String, V>> list, int id, int numItems) {
+
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ Map<String, V> map = list.get(i);
+ for (int j = 0; j < numItems; j++) {
+ String key = Integer.toString(j);
+ assertTrue(map.containsKey(key));
+ assertEquals(j, map.get(key));
+ }
+ }
+ }
+
+ private void validateListOfStruct(List<SmallStruct> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ SmallStruct ss = testData.createSmallStruct(i);
+ for (int j = 0; j < numItems; j++) {
+ assertEquals(ss, list.get(i));
+ }
+ }
+ }
+
+ private void validateListOfBinary(List<ByteBuffer> list, int id, int numItems) {
+ validateNotNullAndNotEmpty(list, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ ByteBuffer bb = ByteBuffer.wrap(testData.BYTES);
+ assertTrue(bb.compareTo(list.get(i)) == 0);
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Set validation helpers.
+
+ private <V extends Number> void validateSet(Set<V> set, Class<V> clasz, int numItems) {
+ validateNotNullAndNotEmpty(set, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ if (clasz == Byte.class) {
+ assertTrue(set.contains((byte)i));
+ } else if (clasz == Short.class) {
+ assertTrue(set.contains((short)i));
+ } else if (clasz == Integer.class) {
+ assertTrue(set.contains(i));
+ } else if (clasz == Long.class) {
+ assertTrue(set.contains((long)i));
+ } else if (clasz == Double.class) {
+ assertTrue(set.contains((double)i));
+ }
+ }
+ }
+
+ private void validateStringSet(Set<String> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ assertTrue(set.contains(Integer.toString(i)));
+ }
+ }
+
+ private void validateEnumSet(Set<TstEnum> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, 1);
+
+ assertTrue(set.contains(TstEnum.E_ONE));
+ }
+
+ private void validateSetOfList(Set<List<Integer>> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, 1);
+
+ List<Integer> list = new ArrayList<>(numItems);
+ for (int i = 0; i < numItems; i++) {
+ list.add(i);
+ }
+
+ assertTrue(set.contains(list));
+ }
+
+ private void validateSetOfSet(Set<Set<Integer>> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, 1);
+
+ Set<Integer> setElt = new HashSet<>();
+ for (int i = 0; i < numItems; i++) {
+ setElt.add(i);
+ }
+
+ assertTrue(set.contains(setElt));
+ }
+
+ private void validateSetOfMap(Set<Map<String, Integer>> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, 1);
+
+ Map<String, Integer> map = new HashMap<>();
+ for (int i = 0; i < numItems; i++) {
+ map.put(Integer.toString(i), i);
+ }
+
+ assertTrue(set.contains(map));
+ }
+
+ private void validateSetOfStruct(Set<SmallStruct> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ SmallStruct ss = testData.createSmallStruct(i);
+ assertTrue(set.contains(ss));
+ }
+ }
+
+ private void validateSetOfBinary(Set<ByteBuffer> set, int id, int numItems) {
+ validateNotNullAndNotEmpty(set, 1);
+
+ for (ByteBuffer b : set) {
+ ByteBuffer bb = ByteBuffer.wrap(testData.BYTES);
+ assertEquals(0, bb.compareTo(b));
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Map validation helpers.
+
+ void validateNotNullAndNotEmpty(Map<?, ?> map, int numItems) {
+ assertNotNull(map);
+ assertEquals(numItems, map.size());
+ }
+
+ private <V extends Number> void validateMap(Map<V, V> map, Class<V> clasz, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ if (clasz == Byte.class) {
+ assertTrue(map.containsKey((byte)i));
+ assertEquals((byte) i, map.get((byte) i));
+ } else if (clasz == Short.class) {
+ assertTrue(map.containsKey((short)i));
+ assertEquals((short) i, map.get((short) i));
+ } else if (clasz == Integer.class) {
+ assertTrue(map.containsKey(i));
+ assertEquals(i, map.get(i));
+ } else if (clasz == Long.class) {
+ assertTrue(map.containsKey((long)i));
+ assertEquals((long) i, map.get((long) i));
+ } else if (clasz == Double.class) {
+ assertTrue(map.containsKey((double)i));
+ assertEquals((double) i, map.get((double) i));
+ }
+ }
+ }
+
+ private void validateStringMap(Map<String, String> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ String key = Integer.toString(i);
+ assertTrue(map.containsKey(key));
+ assertEquals(key, map.get(key));
+ }
+ }
+
+ private void validateEnumMap(Map<TstEnum, TstEnum> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, 1);
+
+ assertTrue(map.containsKey(TstEnum.E_ONE));
+ assertEquals(TstEnum.E_ONE, map.get(TstEnum.E_ONE));
+ }
+
+ private void validateMapOfList(Map<Integer, List<Integer>> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ List<Integer> list = new ArrayList<>(numItems);
+ for (int i = 0; i < numItems; i++) {
+ list.add(i);
+ }
+
+ for (int i = 0; i < numItems; i++) {
+ assertTrue(map.containsKey(i));
+ assertEquals(list, map.get(i));
+ }
+ }
+
+ private void validateMapOfSet(Map<Integer, Set<Integer>> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ Set<Integer> setElt = new HashSet<>();
+ for (int i = 0; i < numItems; i++) {
+ setElt.add(i);
+ }
+
+ for (int i = 0; i < numItems; i++) {
+ assertTrue(map.containsKey(i));
+ assertEquals(setElt, map.get(i));
+ }
+ }
+
+ private void validateMapOfMap(Map<Integer, Map<Integer, Integer>> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ Map<Integer, Integer> mapElt = new HashMap<>();
+ for (int i = 0; i < numItems; i++) {
+ mapElt.put(i, i);
+ }
+
+ for (int i = 0; i < numItems; i++) {
+ assertTrue(map.containsKey(i));
+ assertEquals(mapElt, map.get(i));
+ }
+ }
+
+ private void validateMapOfStruct(Map<SmallStruct, SmallStruct> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ SmallStruct ss = testData.createSmallStruct(i);
+ assertTrue(map.containsKey(ss));
+ assertEquals(ss, map.get(ss));
+ }
+ }
+
+ private void validateMapOfBinary(Map<Integer, ByteBuffer> map, int id, int numItems) {
+ validateNotNullAndNotEmpty(map, numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ ByteBuffer bb = ByteBuffer.wrap(testData.BYTES);
+ assertTrue(map.containsKey(i));
+ assertEquals(0, bb.compareTo(map.get(i)));
+ }
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/EnumCacheTest.java b/lib/java/test/org/apache/thrift/partial/EnumCacheTest.java
new file mode 100644
index 000000000..394dcc2e9
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/EnumCacheTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import org.apache.thrift.partial.ExceptionAsserts;
+
+import org.apache.thrift.TEnum;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test ThriftCodec serializes and deserializes thrift objects correctly.
+ */
+public class EnumCacheTest {
+
+ enum TestEnum implements TEnum {
+ Alice(-1),
+ Bob(0),
+ Charlie(1);
+
+ private int value;
+
+ TestEnum(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public int getValue() {
+ return this.value;
+ }
+ }
+
+ static class NotEnum implements TEnum {
+
+ public static final NotEnum Alice = new NotEnum(-11);
+ public static final NotEnum Bob = new NotEnum(10);
+ public static final NotEnum Charlie = new NotEnum(11);
+
+ private static final NotEnum[] allValues = { Alice, Bob, Charlie };
+
+ private int value;
+
+ private NotEnum(int value) {
+ this.value = value;
+ }
+
+ public static TEnum[] values() {
+ return NotEnum.allValues;
+ }
+
+ @Override
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NotEnum : %d", this.value);
+ }
+ }
+
+ @Test
+ public void testArgChecks() {
+ EnumCache cache = new EnumCache();
+
+ // Should not throw.
+ cache.get(TestEnum.class, 0);
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'enumClass' must not be null",
+ () -> cache.get(null, 1));
+ }
+
+ @Test
+ public void testGet() {
+ EnumCache cache = new EnumCache();
+
+ assertEquals(TestEnum.Alice, cache.get(TestEnum.class, -1));
+ assertEquals(TestEnum.Bob, cache.get(TestEnum.class, 0));
+ assertEquals(TestEnum.Charlie, cache.get(TestEnum.class, 1));
+
+ assertEquals(NotEnum.Alice, cache.get(NotEnum.class, -11));
+ assertEquals(NotEnum.Bob, cache.get(NotEnum.class, 10));
+ assertEquals(NotEnum.Charlie, cache.get(NotEnum.class, 11));
+ }
+
+ @Test
+ public void testGetInvalid() {
+ EnumCache cache = new EnumCache();
+
+ assertNull(cache.get(TestEnum.class, 42));
+ assertNull(cache.get(NotEnum.class, 42));
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ExceptionAsserts.java b/lib/java/test/org/apache/thrift/partial/ExceptionAsserts.java
new file mode 100644
index 000000000..239903cf4
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ExceptionAsserts.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public final class ExceptionAsserts {
+ private ExceptionAsserts() {}
+
+ @FunctionalInterface
+ public interface CodeThatMayThrow {
+ void run() throws Exception;
+ }
+
+ /**
+ * Asserts that the given code throws an exception of the given type
+ * and that the exception message contains the given sub-message.
+ *
+ * Usage:
+ *
+ * ExceptionAsserts.assertThrows(
+ * IllegalArgumentException.class,
+ * "'nullArg' must not be null",
+ * () -> Preconditions.checkNotNull(null, "nullArg"));
+ *
+ * Note: JUnit 5 has similar functionality but it will be a long time before
+ * we move to that framework because of significant differences and lack of
+ * backward compatibility for some JUnit rules.
+ */
+ public static <E extends Exception> void assertThrows(
+ Class<E> expectedExceptionClass,
+ String partialMessage,
+ CodeThatMayThrow code) {
+
+ Exception thrownException = null;
+
+ try {
+ code.run();
+ } catch (Exception e) {
+ if (expectedExceptionClass.isAssignableFrom(e.getClass())) {
+
+ thrownException = e;
+
+ if (partialMessage != null) {
+ String msg = e.getMessage();
+ assertNotNull(
+ String.format("Exception message is null, expected to contain: '%s'", partialMessage),
+ msg);
+ assertTrue(
+ String.format("Exception message '%s' does not contain: '%s'", msg, partialMessage),
+ msg.contains(partialMessage));
+ }
+ } else {
+ fail(String.format(
+ "Expected exception of type %s but got %s",
+ expectedExceptionClass.getName(),
+ e.getClass().getName()));
+ }
+ }
+
+ if (thrownException == null) {
+ fail(String.format(
+ "Expected exception of type %s but got none",
+ expectedExceptionClass.getName()));
+ }
+ }
+
+ public static <E extends Exception> void assertThrows(
+ Class<E> expectedExceptionClass,
+ CodeThatMayThrow code) {
+ assertThrows(expectedExceptionClass, null, code);
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/PartialThriftComparerTest.java b/lib/java/test/org/apache/thrift/partial/PartialThriftComparerTest.java
new file mode 100644
index 000000000..e1209d733
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/PartialThriftComparerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.*;
+
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.partial.TestStruct;
+import org.apache.thrift.partial.ThriftField;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class PartialThriftComparerTest {
+
+ private ThriftSerDe serde;
+ private PartialThriftTestData testData = new PartialThriftTestData();
+
+ public PartialThriftComparerTest() throws TException {
+ this.serde = new ThriftSerDe();
+ }
+
+ @Test
+ public void testCompareSimple() throws TException, IOException {
+ TestStruct ts1 = testData.createTestStruct(1, 1);
+ assertTrue(ts1.isSetI16Field());
+ assertTrue(ts1.isSetI32Field());
+
+ byte[] bytesBinary = serde.serializeBinary(ts1);
+ byte[] bytesCompact = serde.serializeCompact(ts1);
+
+ List<String> fieldNames = Arrays.asList("i32Field");
+ TDeserializer partialBinaryDeser =
+ new TDeserializer(TestStruct.class, fieldNames, new TBinaryProtocol.Factory());
+ TDeserializer partialCompactDeser =
+ new TDeserializer(TestStruct.class, fieldNames, new TCompactProtocol.Factory());
+
+ ThriftMetadata.ThriftStruct metadata = partialBinaryDeser.getMetadata();
+ PartialThriftComparer comparer = new PartialThriftComparer(metadata);
+
+ StringBuilder sb = new StringBuilder();
+ TestStruct ts2 = (TestStruct) partialBinaryDeser.partialDeserializeObject(bytesBinary);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+
+ ts2 = (TestStruct) partialCompactDeser.partialDeserializeObject(bytesCompact);
+ if (!comparer.areEqual(ts1, ts2, sb)) {
+ fail(sb.toString());
+ }
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/PartialThriftTestData.java b/lib/java/test/org/apache/thrift/partial/PartialThriftTestData.java
new file mode 100644
index 000000000..6376075da
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/PartialThriftTestData.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helpers for creating test data related to partial deserialization.
+ */
+public class PartialThriftTestData {
+
+ public final byte[] BYTES = new byte[] { 1, 2, 3 };
+
+ public SmallStruct createSmallStruct(int id) {
+ return new SmallStruct()
+ .setByteField((byte) id)
+ .setI16Field((short) id)
+ .setI32Field(id)
+ .setI64Field(id)
+ .setDoubleField(id)
+ .setStringField(Integer.toString(id))
+ .setEnumField(TstEnum.E_ONE);
+ }
+
+ public TestStruct createTestStruct(int id, int numItems) {
+
+ TestStruct ts = new TestStruct()
+ .setByteField((byte) id)
+ .setI16Field((short) id)
+ .setI32Field(id)
+ .setI64Field(id)
+ .setDoubleField(id)
+ .setStringField(Integer.toString(id))
+ .setEnumField(TstEnum.E_ONE)
+ .setBinaryField(BYTES)
+ .setStructField(createSmallStruct(id));
+
+ initListFields(ts, id, numItems);
+ initSetFields(ts, id, numItems);
+ initMapFields(ts, id, numItems);
+
+ return ts;
+ }
+
+ public void initListFields(TestStruct ts, int id, int numItems) {
+ List<Byte> byteList = new ArrayList<>(numItems);
+ List<Short> i16List = new ArrayList<>(numItems);
+ List<Integer> i32List = new ArrayList<>(numItems);
+ List<Long> i64List = new ArrayList<>(numItems);
+ List<Double> doubleList = new ArrayList<>(numItems);
+ List<String> stringList = new ArrayList<>(numItems);
+ List<TstEnum> enumList = new ArrayList<>(numItems);
+
+ List<List<Integer>> listList = new ArrayList<>(numItems);
+ List<Set<Integer>> setList = new ArrayList<>(numItems);
+ List<Map<String, Integer>> mapList = new ArrayList<>(numItems);
+ List<SmallStruct> structList = new ArrayList<>(numItems);
+ List<ByteBuffer> binaryList = new ArrayList<>(numItems);
+
+ for (int i = 0; i < numItems; i++) {
+ byteList.add((byte) i);
+ i16List.add((short) i);
+ i32List.add(i);
+ i64List.add((long)i);
+ doubleList.add((double) i);
+ stringList.add(Integer.toString(i));
+ enumList.add(TstEnum.E_ONE);
+ structList.add(createSmallStruct(i));
+ binaryList.add(ByteBuffer.wrap(BYTES));
+
+ List<Integer> listItem = new ArrayList<>(numItems);
+ listList.add(listItem);
+
+ Set<Integer> setItem = new HashSet<>();
+ setList.add(setItem);
+
+ Map<String, Integer> mapItem = new HashMap<>();
+ mapList.add(mapItem);
+
+ for (int j = 0; j < numItems; j++) {
+ listItem.add(j);
+ setItem.add(j);
+ mapItem.put(Integer.toString(j), j);
+ }
+ }
+
+ ts.setByteList(byteList)
+ .setI16List(i16List)
+ .setI32List(i32List)
+ .setI64List(i64List)
+ .setDoubleList(doubleList)
+ .setStringList(stringList)
+ .setEnumList(enumList)
+ .setListList(listList)
+ .setSetList(setList)
+ .setMapList(mapList)
+ .setStructList(structList)
+ .setBinaryList(binaryList);
+ }
+
+ public void initSetFields(TestStruct ts, int id, int numItems) {
+ Set<Byte> byteSet = new HashSet<>();
+ Set<Short> i16Set = new HashSet<>();
+ Set<Integer> i32Set = new HashSet<>();
+ Set<Long> i64Set = new HashSet<>();
+ Set<Double> doubleSet = new HashSet<>();
+ Set<String> stringSet = new HashSet<>();
+ Set<TstEnum> enumSet = new HashSet<>();
+
+ Set<List<Integer>> listSet = new HashSet<>();
+ Set<Set<Integer>> setSet = new HashSet<>();
+ Set<Map<String, Integer>> mapSet = new HashSet<>();
+ Set<SmallStruct> structSet = new HashSet<>();
+ Set<ByteBuffer> binarySet = new HashSet<>();
+
+ for (int i = 0; i < numItems; i++) {
+ byteSet.add((byte) i);
+ i16Set.add((short) i);
+ i32Set.add(i);
+ i64Set.add((long)i);
+ doubleSet.add((double) i);
+ stringSet.add(Integer.toString(i));
+ enumSet.add(TstEnum.E_ONE);
+ structSet.add(createSmallStruct(i));
+ binarySet.add(ByteBuffer.wrap(BYTES));
+
+ List<Integer> listItem = new ArrayList<>(numItems);
+ Set<Integer> setItem = new HashSet<>();
+ Map<String, Integer> mapItem = new HashMap<>();
+
+ for (int j = 0; j < numItems; j++) {
+ setItem.add(j);
+ listItem.add(j);
+ mapItem.put(Integer.toString(j), j);
+ }
+
+ listSet.add(listItem);
+ setSet.add(setItem);
+ mapSet.add(mapItem);
+ }
+
+ ts.setByteSet(byteSet)
+ .setI16Set(i16Set)
+ .setI32Set(i32Set)
+ .setI64Set(i64Set)
+ .setDoubleSet(doubleSet)
+ .setStringSet(stringSet)
+ .setEnumSet(enumSet)
+ .setListSet(listSet)
+ .setSetSet(setSet)
+ .setMapSet(mapSet)
+ .setStructSet(structSet)
+ .setBinarySet(binarySet);
+ }
+
+ public void initMapFields(TestStruct ts, int id, int numItems) {
+ Map<Byte, Byte> byteMap = new HashMap<>();
+ Map<Short, Short> i16Map = new HashMap<>();
+ Map<Integer, Integer> i32Map = new HashMap<>();
+ Map<Long, Long> i64Map = new HashMap<>();
+ Map<Double, Double> doubleMap = new HashMap<>();
+ Map<String, String> stringMap = new HashMap<>();
+ Map<TstEnum, TstEnum> enumMap = new HashMap<>();
+
+ Map<Integer, List<Integer>> listMap = new HashMap<>();
+ Map<Integer, Set<Integer>> setMap = new HashMap<>();
+ Map<Integer, Map<Integer, Integer>> mapMap = new HashMap<>();
+ Map<SmallStruct, SmallStruct> structMap = new HashMap<>();
+ Map<Integer, ByteBuffer> binaryMap = new HashMap<>();
+
+ for (int i = 0; i < numItems; i++) {
+ byteMap.put((byte) i, (byte) i);
+ i16Map.put((short) i, (short) i);
+ i32Map.put(i, i);
+ i64Map.put((long) i, (long) i);
+ doubleMap.put((double) i, (double) i);
+ stringMap.put(Integer.toString(i), Integer.toString(i));
+ enumMap.put(TstEnum.E_ONE, TstEnum.E_ONE);
+ structMap.put(createSmallStruct(i), createSmallStruct(i));
+ binaryMap.put(i, ByteBuffer.wrap(BYTES));
+
+ List<Integer> listItem = new ArrayList<>(numItems);
+ listMap.put(i, listItem);
+
+ Set<Integer> setItem = new HashSet<>();
+ setMap.put(i, setItem);
+
+ Map<Integer, Integer> mapItem = new HashMap<>();
+ mapMap.put(i, mapItem);
+
+ for (int j = 0; j < numItems; j++) {
+ listItem.add(j);
+ setItem.add(j);
+ mapItem.put(j, j);
+ }
+ }
+
+ ts.setByteMap(byteMap)
+ .setI16Map(i16Map)
+ .setI32Map(i32Map)
+ .setI64Map(i64Map)
+ .setDoubleMap(doubleMap)
+ .setStringMap(stringMap)
+ .setEnumMap(enumMap)
+ .setListMap(listMap)
+ .setSetMap(setMap)
+ .setMapMap(mapMap)
+ .setStructMap(structMap)
+ .setBinaryMap(binaryMap);
+ }
+
+ public List<String> allFieldsOfTestStruct() {
+ return new ArrayList<>(
+ Arrays.asList(
+ "byteField",
+ "i16Field",
+ "i32Field",
+ "i64Field",
+ "doubleField",
+ "stringField",
+ "structField.byteField",
+ "structField.i16Field",
+ "structField.i32Field",
+ "structField.i64Field",
+ "structField.doubleField",
+ "structField.stringField",
+ "structField.enumField",
+ "enumField",
+ "binaryField",
+ "byteList",
+ "i16List",
+ "i32List",
+ "i64List",
+ "doubleList",
+ "stringList",
+ "enumList",
+ "listList",
+ "setList",
+ "mapList",
+ "structList.byteField",
+ "structList.i16Field",
+ "structList.i32Field",
+ "structList.i64Field",
+ "structList.doubleField",
+ "structList.stringField",
+ "structList.enumField",
+ "binaryList",
+ "byteSet",
+ "i16Set",
+ "i32Set",
+ "i64Set",
+ "doubleSet",
+ "stringSet",
+ "enumSet",
+ "listSet",
+ "setSet",
+ "mapSet",
+ "structSet.byteField",
+ "structSet.i16Field",
+ "structSet.i32Field",
+ "structSet.i64Field",
+ "structSet.doubleField",
+ "structSet.stringField",
+ "structSet.enumField",
+ "binarySet",
+ "byteMap",
+ "i16Map",
+ "i32Map",
+ "i64Map",
+ "doubleMap",
+ "stringMap",
+ "enumMap",
+ "listMap",
+ "setMap",
+ "mapMap",
+ "structMap.byteField",
+ "structMap.i16Field",
+ "structMap.i32Field",
+ "structMap.i64Field",
+ "structMap.doubleField",
+ "structMap.stringField",
+ "structMap.enumField",
+ "binaryMap"
+ )
+ );
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/TFieldDataTest.java b/lib/java/test/org/apache/thrift/partial/TFieldDataTest.java
new file mode 100644
index 000000000..0a838e970
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/TFieldDataTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TType;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class TFieldDataTest {
+
+ @Test
+ public void testEncodeStop() {
+ TField field = new TField("", TType.STOP, (short) 0);
+ int data = TFieldData.encode(TType.STOP);
+
+ assertEquals(field.type, TFieldData.getType(data));
+ assertEquals(field.id, TFieldData.getId(data));
+ }
+
+ @Test
+ public void testEncodeRest() {
+ for (byte type = 1; type <= 16; type++) {
+ for (short id = 0; id < Short.MAX_VALUE; id++) {
+ TField field = new TField("", type, id);
+ int data = TFieldData.encode(type, id);
+
+ assertEquals(field.type, TFieldData.getType(data));
+ assertEquals(field.id, TFieldData.getId(data));
+ }
+ }
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/TestData.java b/lib/java/test/org/apache/thrift/partial/TestData.java
new file mode 100644
index 000000000..1779346f7
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/TestData.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Frequently used test data items.
+ */
+public final class TestData {
+ private TestData() {}
+
+
+ // Array data.
+ public static Object[] nullArray = null;
+ public static Object[] emptyArray = new Object[0];
+ public static Object[] nonEmptyArray = new Object[1];
+
+ public static byte[] nullByteArray = null;
+ public static byte[] emptyByteArray = new byte[0];
+ public static byte[] nonEmptyByteArray = new byte[1];
+
+ public static short[] nullShortArray = null;
+ public static short[] emptyShortArray = new short[0];
+ public static short[] nonEmptyShortArray = new short[1];
+
+ public static int[] nullIntArray = null;
+ public static int[] emptyIntArray = new int[0];
+ public static int[] nonEmptyIntArray = new int[1];
+
+ public static long[] nullLongArray = null;
+ public static long[] emptyLongArray = new long[0];
+ public static long[] nonEmptyLongArray = new long[1];
+
+ public static List<Object> nullList = null;
+ public static List<Object> emptyList = new ArrayList<Object>();
+ public static List<Object> validList = Arrays.asList(new Object[1]);
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ThriftFieldTest.java b/lib/java/test/org/apache/thrift/partial/ThriftFieldTest.java
new file mode 100644
index 000000000..a6d5655e4
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ThriftFieldTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.*;
+
+import org.apache.thrift.partial.ExceptionAsserts;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class ThriftFieldTest {
+
+ @Test
+ public void testArgChecks() {
+ ThriftField test;
+ List<ThriftField> testFields;
+
+ // Should not throw.
+ test = new ThriftField("foo");
+ test = new ThriftField("foo", Arrays.asList(new ThriftField("bar")));
+ testFields = ThriftField.fromNames(Arrays.asList("foo"));
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'name' must not be null",
+ () -> new ThriftField(null, Collections.emptyList()));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'fields' must not be null",
+ () -> new ThriftField("foo", null));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'fieldNames' must not be null",
+ () -> ThriftField.fromNames(null));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'fieldNames' must have at least one element",
+ () -> ThriftField.fromNames(Collections.emptyList()));
+ }
+
+ @Test
+ public void testFromNames() {
+ List<String> fieldNames = Arrays.asList(
+ "f1",
+ "f2.f21",
+ "f3.f31.f311",
+ "f3.f32.f321",
+ "f3.f32.f322"
+ );
+
+ List<ThriftField> testFields = ThriftField.fromNames(fieldNames);
+
+ assertEquals(3, testFields.size());
+ ThriftField f1 = testFields.get(0);
+ ThriftField f2 = testFields.get(1);
+ ThriftField f3 = testFields.get(2);
+ assertEquals("f1", f1.name);
+ assertEquals("f2", f2.name);
+ assertEquals("f3", f3.name);
+
+ assertEquals(0, f1.fields.size());
+ assertEquals(1, f2.fields.size());
+ assertEquals(2, f3.fields.size());
+
+ ThriftField f21 = f2.fields.get(0);
+ ThriftField f31 = f3.fields.get(0);
+ ThriftField f32 = f3.fields.get(1);
+ assertEquals("f21", f21.name);
+ assertEquals("f31", f31.name);
+ assertEquals("f32", f32.name);
+
+ assertEquals(0, f21.fields.size());
+ assertEquals(1, f31.fields.size());
+ assertEquals(2, f32.fields.size());
+
+ ThriftField f311 = f31.fields.get(0);
+ ThriftField f321 = f32.fields.get(0);
+ ThriftField f322 = f32.fields.get(1);
+ assertEquals("f311", f311.name);
+ assertEquals("f321", f321.name);
+ assertEquals("f322", f322.name);
+
+ assertEquals(0, f311.fields.size());
+ assertEquals(0, f321.fields.size());
+ assertEquals(0, f322.fields.size());
+ }
+
+ @Test
+ public void testEquality() {
+ List<String> fieldNames = Arrays.asList(
+ "f1",
+ "f2.f21",
+ "f3.f31.f311",
+ "f3.f32.f321",
+ "f3.f32.f322"
+ );
+
+ List<ThriftField> testFields = ThriftField.fromNames(fieldNames);
+ List<ThriftField> testFields2 = testFields;
+
+ assertSame(testFields, testFields2);
+ assertEquals(testFields, testFields2);
+
+ List<ThriftField> testFields3 = ThriftField.fromNames(fieldNames);
+ assertNotSame(testFields, testFields3);
+ assertEquals(testFields, testFields3);
+ assertEquals(testFields.hashCode(), testFields3.hashCode());
+
+ List<String> fieldNamesDiff = Arrays.asList(
+ "f1",
+ "f2.f21",
+ "f3.f31.f311",
+ "f3.f32.f323",
+ "f3.f32.f322"
+ );
+
+ List<ThriftField> testFields4 = ThriftField.fromNames(fieldNamesDiff);
+ assertNotSame(testFields, testFields4);
+ assertNotEquals(testFields, testFields4);
+ assertNotEquals(testFields.hashCode(), testFields4.hashCode());
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ThriftMetadataTest.java b/lib/java/test/org/apache/thrift/partial/ThriftMetadataTest.java
new file mode 100644
index 000000000..acc53c8a6
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ThriftMetadataTest.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.*;
+
+import org.apache.thrift.partial.TestStruct;
+import org.apache.thrift.partial.ThriftField;
+import org.apache.thrift.partial.ExceptionAsserts;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.meta_data.EnumMetaData;
+import org.apache.thrift.meta_data.FieldValueMetaData;
+import org.apache.thrift.meta_data.ListMetaData;
+import org.apache.thrift.meta_data.MapMetaData;
+import org.apache.thrift.meta_data.SetMetaData;
+import org.apache.thrift.meta_data.StructMetaData;
+import org.apache.thrift.protocol.TType;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ThriftMetadataTest {
+
+ private PartialThriftTestData testData = new PartialThriftTestData();
+
+ @Test
+ public void testArgChecks() {
+ // Should not throw.
+ List<ThriftField> testFields = ThriftField.fromNames(Arrays.asList("byteField"));
+ ThriftMetadata.ThriftStruct.fromFields(TestStruct.class, testFields);
+
+ // Verify it throws correctly.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'clasz' must not be null",
+ () -> ThriftMetadata.ThriftStruct.fromFields(null, testFields));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'fields' must not be null",
+ () -> ThriftMetadata.ThriftStruct.fromFields(TestStruct.class, null));
+ }
+
+ @Test
+ public void testThriftStructOf() {
+ ThriftMetadata.ThriftStruct testStruct = ThriftMetadata.ThriftStruct.of(TestStruct.class);
+ assertEquals(45, testStruct.fields.keySet().size());
+ validateFieldMetadata(testStruct, 1, "byteField", TType.BYTE);
+ validateFieldMetadata(testStruct, 2, "i16Field", TType.I16);
+ validateFieldMetadata(testStruct, 3, "i32Field", TType.I32);
+ validateFieldMetadata(testStruct, 4, "i64Field", TType.I64);
+ validateFieldMetadata(testStruct, 5, "doubleField", TType.DOUBLE);
+ validateFieldMetadata(testStruct, 6, "stringField", TType.STRING);
+ validateFieldMetadata(testStruct, 7, "enumField", TType.ENUM);
+ validateFieldMetadata(testStruct, 8, "binaryField", TType.STRING);
+
+ validateListFieldMetadata(testStruct, 10, "byteList", TType.BYTE);
+ validateSetFieldMetadata(testStruct, 35, "stringSet", TType.STRING);
+ validateMapFieldMetadata(testStruct, 61, "binaryMap", TType.I32, TType.STRING);
+ }
+
+ @Test
+ public void testUnion() {
+ ThriftMetadata.ThriftStruct structWithUnions =
+ ThriftMetadata.ThriftStruct.of(StructWithUnions.class);
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 1, "intValue");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 2, "smallStruct");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 3, "simpleUnion");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 4, "unionList");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 5, "unionSet");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 6, "keyUnionMap");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 7, "valUnionMap");
+ validateBasicFieldMetadata(structWithUnions, StructWithUnions.class, 8, "unionMap");
+
+ ThriftMetadata.ThriftStructBase smallStructMetadata =
+ (ThriftMetadata.ThriftStructBase) structWithUnions.fields.get(2);
+ assertFalse(smallStructMetadata.isUnion());
+
+ ThriftMetadata.ThriftStructBase simpleUnionMetadata =
+ (ThriftMetadata.ThriftStructBase) structWithUnions.fields.get(3);
+ assertTrue(simpleUnionMetadata.isUnion());
+
+ ThriftMetadata.ThriftList unionListMetadata =
+ (ThriftMetadata.ThriftList) structWithUnions.fields.get(4);
+ assertTrue(unionListMetadata.hasUnion());
+
+ ThriftMetadata.ThriftSet unionSetMetadata =
+ (ThriftMetadata.ThriftSet) structWithUnions.fields.get(5);
+ assertTrue(unionSetMetadata.hasUnion());
+
+ ThriftMetadata.ThriftMap keyUnionMapMetadata =
+ (ThriftMetadata.ThriftMap) structWithUnions.fields.get(6);
+ assertTrue(keyUnionMapMetadata.hasUnion());
+
+ ThriftMetadata.ThriftMap valUnionMapMetadata =
+ (ThriftMetadata.ThriftMap) structWithUnions.fields.get(7);
+ assertTrue(valUnionMapMetadata.hasUnion());
+
+ ThriftMetadata.ThriftMap unionMapMetadata =
+ (ThriftMetadata.ThriftMap) structWithUnions.fields.get(8);
+ assertTrue(unionMapMetadata.hasUnion());
+ }
+
+ private ThriftMetadata.ThriftObject validateBasicFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ int id,
+ String fieldName) {
+ return validateBasicFieldMetadata(testStruct, TestStruct.class, id, fieldName);
+ }
+
+ private ThriftMetadata.ThriftObject validateBasicFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ Class<? extends TBase> clazz,
+ int id,
+ String fieldName) {
+
+ assertNotNull(testStruct);
+ assertNull(testStruct.parent);
+ assertEquals(clazz, ((StructMetaData) testStruct.data.valueMetaData).structClass);
+ assertTrue(testStruct.fields.containsKey(id));
+
+ ThriftMetadata.ThriftObject fieldMetadata =
+ (ThriftMetadata.ThriftObject) testStruct.fields.get(id);
+ assertEquals(testStruct, fieldMetadata.parent);
+
+ assertEquals(id, fieldMetadata.fieldId.getThriftFieldId());
+ assertEquals(fieldName, fieldMetadata.fieldId.getFieldName());
+ assertEquals(fieldName, fieldMetadata.data.fieldName);
+
+ assertEquals("root ==> " + fieldName, fieldMetadata.toString());
+
+ return fieldMetadata;
+ }
+
+ private void validateBasicFieldValueMetadata(
+ ThriftMetadata.ThriftObject fieldMetadata,
+ String fieldName,
+ byte ttype) {
+
+ assertEquals(ttype, fieldMetadata.data.valueMetaData.type);
+ assertEquals(getMetaDataClassForTType(ttype), fieldMetadata.data.valueMetaData.getClass());
+ Class<? extends ThriftMetadata.ThriftObject> fieldMetadataClass = getClassForTType(ttype);
+ assertEquals(fieldMetadataClass, fieldMetadata.getClass());
+ if (fieldMetadataClass == ThriftMetadata.ThriftPrimitive.class) {
+ ThriftMetadata.ThriftPrimitive primitive
+ = (ThriftMetadata.ThriftPrimitive) fieldMetadata;
+ if (fieldName.startsWith("binary") && (ttype == TType.STRING)) {
+ assertTrue(primitive.isBinary());
+ } else {
+ assertFalse(primitive.isBinary());
+ }
+ }
+ }
+
+ private void validateFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ int id,
+ String fieldName,
+ byte ttype) {
+
+ ThriftMetadata.ThriftObject fieldMetadata =
+ validateBasicFieldMetadata(testStruct, id, fieldName);
+ validateBasicFieldValueMetadata(fieldMetadata, fieldName, ttype);
+ }
+
+ private void validateListFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ int id,
+ String fieldName,
+ byte ttype) {
+
+ ThriftMetadata.ThriftObject fieldMetadata =
+ validateBasicFieldMetadata(testStruct, id, fieldName);
+ validateBasicFieldValueMetadata(fieldMetadata, fieldName, TType.LIST);
+
+ ThriftMetadata.ThriftList thriftList = (ThriftMetadata.ThriftList) fieldMetadata;
+ ThriftMetadata.ThriftObject elementMetadata = thriftList.elementData;
+ validateBasicFieldValueMetadata(elementMetadata, fieldName + "_element", ttype);
+ }
+
+ private void validateSetFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ int id,
+ String fieldName,
+ byte ttype) {
+
+ ThriftMetadata.ThriftObject fieldMetadata =
+ validateBasicFieldMetadata(testStruct, id, fieldName);
+ validateBasicFieldValueMetadata(fieldMetadata, fieldName, TType.SET);
+
+ ThriftMetadata.ThriftSet thriftSet = (ThriftMetadata.ThriftSet) fieldMetadata;
+ ThriftMetadata.ThriftObject elementMetadata = thriftSet.elementData;
+ validateBasicFieldValueMetadata(elementMetadata, fieldName + "_element", ttype);
+ }
+
+ private void validateMapFieldMetadata(
+ ThriftMetadata.ThriftStruct testStruct,
+ int id,
+ String fieldName,
+ byte keyType,
+ byte valueType) {
+
+ ThriftMetadata.ThriftObject fieldMetadata =
+ validateBasicFieldMetadata(testStruct, id, fieldName);
+ validateBasicFieldValueMetadata(fieldMetadata, fieldName, TType.MAP);
+
+ ThriftMetadata.ThriftMap thriftMap = (ThriftMetadata.ThriftMap) fieldMetadata;
+ ThriftMetadata.ThriftObject keyMetadata = thriftMap.keyData;
+ ThriftMetadata.ThriftObject valueMetadata = thriftMap.valueData;
+ validateBasicFieldValueMetadata(keyMetadata, fieldName + "_key", keyType);
+ validateBasicFieldValueMetadata(valueMetadata, fieldName + "_value", valueType);
+ }
+
+ private Class<? extends FieldValueMetaData> getMetaDataClassForTType(byte ttype) {
+ switch (ttype) {
+ case TType.STRUCT:
+ return StructMetaData.class;
+
+ case TType.LIST:
+ return ListMetaData.class;
+
+ case TType.MAP:
+ return MapMetaData.class;
+
+ case TType.SET:
+ return SetMetaData.class;
+
+ case TType.ENUM:
+ return EnumMetaData.class;
+
+ case TType.BOOL:
+ case TType.BYTE:
+ case TType.I16:
+ case TType.I32:
+ case TType.I64:
+ case TType.DOUBLE:
+ case TType.STRING:
+ return FieldValueMetaData.class;
+
+ default:
+ throw ThriftMetadata.unsupportedFieldTypeException(ttype);
+ }
+ }
+
+ private Class<? extends ThriftMetadata.ThriftObject> getClassForTType(byte ttype) {
+ switch (ttype) {
+ case TType.STRUCT:
+ return ThriftMetadata.ThriftStruct.class;
+
+ case TType.LIST:
+ return ThriftMetadata.ThriftList.class;
+
+ case TType.MAP:
+ return ThriftMetadata.ThriftMap.class;
+
+ case TType.SET:
+ return ThriftMetadata.ThriftSet.class;
+
+ case TType.ENUM:
+ return ThriftMetadata.ThriftEnum.class;
+
+ case TType.BOOL:
+ case TType.BYTE:
+ case TType.I16:
+ case TType.I32:
+ case TType.I64:
+ case TType.DOUBLE:
+ case TType.STRING:
+ return ThriftMetadata.ThriftPrimitive.class;
+
+ default:
+ throw ThriftMetadata.unsupportedFieldTypeException(ttype);
+ }
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ThriftSerDe.java b/lib/java/test/org/apache/thrift/partial/ThriftSerDe.java
new file mode 100644
index 000000000..361c32c60
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ThriftSerDe.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TCompactProtocol;
+
+public class ThriftSerDe {
+ private TSerializer binarySerializer;
+ private TSerializer compactSerializer;
+ private TDeserializer binaryDeserializer;
+ private TDeserializer compactDeserializer;
+
+ public ThriftSerDe() throws TException {
+ this.binarySerializer = new TSerializer(new TBinaryProtocol.Factory());
+ this.compactSerializer = new TSerializer(new TCompactProtocol.Factory());
+ this.binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory());
+ this.compactDeserializer = new TDeserializer(new TCompactProtocol.Factory());
+ }
+
+ public byte[] serializeBinary(TBase obj) throws TException {
+ return binarySerializer.serialize(obj);
+ }
+
+ public byte[] serializeCompact(TBase obj) throws TException {
+ return compactSerializer.serialize(obj);
+ }
+
+ public <T extends TBase> T deserializeBinary(byte[] bytes, Class<T> clazz) throws TException {
+ T instance = this.newInstance(clazz);
+ binaryDeserializer.deserialize(instance, bytes);
+ return clazz.cast(instance);
+ }
+
+ public <T extends TBase> T deserializeCompact(byte[] bytes, Class<T> clazz) throws TException {
+ T instance = this.newInstance(clazz);
+ compactDeserializer.deserialize(instance, bytes);
+ return clazz.cast(instance);
+ }
+
+ private <T extends TBase> T newInstance(Class<T> clazz) {
+ T instance = null;
+ try {
+ instance = clazz.newInstance();
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ }
+ return clazz.cast(instance);
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ThriftStructProcessorTest.java b/lib/java/test/org/apache/thrift/partial/ThriftStructProcessorTest.java
new file mode 100644
index 000000000..d4ab92509
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ThriftStructProcessorTest.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import static org.junit.Assert.*;
+
+import org.apache.thrift.partial.TestStruct;
+import org.apache.thrift.partial.ThriftField;
+import org.apache.thrift.partial.ThriftMetadata;
+import org.apache.thrift.partial.TstEnum;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.TException;
+import org.apache.thrift.TFieldIdEnum;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ThriftStructProcessorTest {
+
+ private PartialThriftTestData testData = new PartialThriftTestData();
+
+ @Test
+ public void testStruct() throws TException {
+ List<ThriftField> fields = ThriftField.fromNames(Arrays.asList("i32Field"));
+ ThriftMetadata.ThriftStruct metadata =
+ ThriftMetadata.ThriftStruct.fromFields(TestStruct.class, fields);
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewStruct(metadata);
+ assertNotNull(instance);
+ assertTrue(instance instanceof TBase);
+ assertTrue(instance instanceof TestStruct);
+
+ Object instance2 = processor.prepareStruct(instance);
+ assertSame(instance, instance2);
+ }
+
+ @Test
+ public void testList() throws TException {
+ final int numItems = 10;
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewList(numItems);
+ assertNotNull(instance);
+ assertTrue(instance instanceof Object[]);
+
+ Object[] items = (Object[]) instance;
+ for (int i = 0; i < numItems; i++) {
+ assertNull(items[i]);
+ processor.setListElement(instance, i, Integer.valueOf(i));
+ assertEquals(i, items[i]);
+ }
+
+ assertTrue(processor.prepareList(instance) instanceof List<?>);
+ }
+
+ @Test
+ public void testMap() throws TException {
+ final int numItems = 10;
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewMap(numItems);
+ assertNotNull(instance);
+ assertTrue(instance instanceof Map<?, ?>);
+
+ Map<Object, Object> items = (Map<Object, Object>) instance;
+ int ignoredIndex = -1;
+ for (int i = 0; i < numItems; i++) {
+ assertNull(items.get(i));
+ processor.setMapElement(instance, ignoredIndex, Integer.valueOf(i), Integer.valueOf(i));
+ assertEquals(i, items.get(i));
+ }
+
+ assertTrue(processor.prepareMap(instance) instanceof Map<?, ?>);
+ }
+
+ @Test
+ public void testSet() throws TException {
+ final int numItems = 10;
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewSet(numItems);
+ assertNotNull(instance);
+ assertTrue(instance instanceof HashSet<?>);
+
+ Set<?> items = (HashSet<?>) instance;
+ int ignoredIndex = -1;
+
+ for (int i = 0; i < numItems; i++) {
+ assertFalse(items.contains(i));
+ processor.setSetElement(instance, ignoredIndex, Integer.valueOf(i));
+ assertTrue(items.contains(i));
+ }
+
+ assertTrue(processor.prepareSet(instance) instanceof Set<?>);
+ }
+
+ @Test
+ public void testPrepareEnum() throws TException {
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.prepareEnum(TstEnum.class, 1);
+ assertNotNull(instance);
+ assertEquals(TstEnum.E_ONE, instance);
+
+ instance = processor.prepareEnum(TstEnum.class, 2);
+ assertNotNull(instance);
+ assertEquals(TstEnum.E_TWO, instance);
+ }
+
+ @Test
+ public void testPrepareString() throws TException {
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ ByteBuffer emptyBuffer = ByteBuffer.wrap(new byte[0]);
+ Object instance = processor.prepareString(emptyBuffer);
+ assertNotNull(instance);
+ assertTrue(instance instanceof String);
+ assertEquals("", instance);
+
+ String value = "Hello world!";
+ ByteBuffer buffer = ByteBuffer.wrap(value.getBytes(StandardCharsets.UTF_8));
+ instance = processor.prepareString(buffer);
+ assertNotNull(instance);
+ assertTrue(instance instanceof String);
+ assertEquals(value, instance);
+ }
+
+ @Test
+ public void testPrepareBinary() throws TException {
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ ByteBuffer emptyBuffer = ByteBuffer.wrap(new byte[0]);
+ Object instance = processor.prepareBinary(emptyBuffer);
+ assertNotNull(instance);
+ assertTrue(instance instanceof ByteBuffer);
+ assertSame(emptyBuffer, instance);
+ }
+
+ @Test
+ public void testStructPrimitiveFields() throws TException {
+ List<ThriftField> fields = ThriftField.fromNames(
+ Arrays.asList(
+ "byteField",
+ "i16Field",
+ "i32Field",
+ "i64Field",
+ "doubleField",
+ "stringField",
+
+ "enumField",
+ "binaryField"
+ ));
+
+ ThriftMetadata.ThriftStruct metadata =
+ ThriftMetadata.ThriftStruct.fromFields(TestStruct.class, fields);
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewStruct(metadata);
+ assertNotNull(instance);
+ assertTrue(instance instanceof TBase);
+ assertTrue(instance instanceof TestStruct);
+
+ TestStruct struct = (TestStruct) instance;
+
+ // byte
+ TFieldIdEnum fieldId = findFieldId(metadata, "byteField");
+ assertNull(getFieldValue(struct, fieldId));
+ processor.setByte(struct, fieldId, (byte) 42);
+ assertEquals(42, struct.getByteField());
+
+ // short
+ fieldId = findFieldId(metadata, "i16Field");
+ assertNull(getFieldValue(struct, fieldId));
+ processor.setInt16(struct, fieldId, (short) 42);
+ assertEquals(42, struct.getI16Field());
+
+ // int
+ fieldId = findFieldId(metadata, "i32Field");
+ assertNull(getFieldValue(struct, fieldId));
+ processor.setInt32(struct, fieldId, 42);
+ assertEquals(42, struct.getI32Field());
+
+ // long
+ fieldId = findFieldId(metadata, "i64Field");
+ assertNull(getFieldValue(struct, fieldId));
+ processor.setInt64(struct, fieldId, 42L);
+ assertEquals(42, struct.getI64Field());
+
+ // binary
+ fieldId = findFieldId(metadata, "binaryField");
+ assertNull(getFieldValue(struct, fieldId));
+ byte[] noBytes = new byte[0];
+ ByteBuffer emptyBuffer = ByteBuffer.wrap(noBytes);
+ processor.setBinary(struct, fieldId, emptyBuffer);
+ assertArrayEquals(noBytes, struct.getBinaryField());
+
+ // string
+ fieldId = findFieldId(metadata, "stringField");
+ assertNull(getFieldValue(struct, fieldId));
+ String value = "Hello world!";
+ ByteBuffer buffer = ByteBuffer.wrap(value.getBytes(StandardCharsets.UTF_8));
+ processor.setString(struct, fieldId, buffer);
+ assertEquals(value, struct.getStringField());
+
+ // enum
+ fieldId = findFieldId(metadata, "enumField");
+ assertNull(getFieldValue(struct, fieldId));
+ TstEnum e1 = TstEnum.E_ONE;
+ processor.setEnumField(struct, fieldId, e1);
+ assertEquals(TstEnum.E_ONE, struct.getEnumField());
+ }
+
+ @Test
+ public void testStructContainerFields() throws TException {
+ List<ThriftField> fields = ThriftField.fromNames(
+ Arrays.asList(
+ // List field
+ "i32List",
+
+ // Set field
+ "stringSet",
+
+ // Map field
+ "stringMap",
+
+ // Struct field
+ "structField"
+ ));
+
+ ThriftMetadata.ThriftStruct metadata =
+ ThriftMetadata.ThriftStruct.fromFields(TestStruct.class, fields);
+ ThriftStructProcessor processor = new ThriftStructProcessor();
+ Object instance = processor.createNewStruct(metadata);
+ assertNotNull(instance);
+ assertTrue(instance instanceof TBase);
+ assertTrue(instance instanceof TestStruct);
+
+ TestStruct struct = (TestStruct) instance;
+
+ // list
+ TFieldIdEnum fieldId = findFieldId(metadata, "i32List");
+ assertNull(getFieldValue(struct, fieldId));
+ Integer[] ints = new Integer[] { 1, 2, 3 };
+ List<Integer> intList = Arrays.asList(ints);
+ processor.setListField(struct, fieldId, intList);
+ assertArrayEquals(ints, struct.getI32List().toArray());
+
+ // set
+ fieldId = findFieldId(metadata, "stringSet");
+ assertNull(getFieldValue(struct, fieldId));
+ String[] strings = new String[] { "Hello", "World!" };
+ Set<String> stringSet = new HashSet<>(Arrays.asList(strings));
+ processor.setSetField(struct, fieldId, stringSet);
+ assertEquals(stringSet, struct.getStringSet());
+
+ // map
+ fieldId = findFieldId(metadata, "stringMap");
+ assertNull(getFieldValue(struct, fieldId));
+ Map<String, String> stringMap = new HashMap<>();
+ stringMap.put("foo", "bar");
+ stringMap.put("Hello", "World!");
+ processor.setMapField(struct, fieldId, stringMap);
+ assertEquals(stringMap, struct.getStringMap());
+
+ // struct
+ fieldId = findFieldId(metadata, "structField");
+ assertNull(getFieldValue(struct, fieldId));
+ SmallStruct smallStruct = new SmallStruct();
+ smallStruct.setI32Field(42);
+ SmallStruct smallStruct2 = new SmallStruct();
+ smallStruct2.setI32Field(42);
+ processor.setStructField(struct, fieldId, smallStruct);
+ assertEquals(smallStruct2, struct.getStructField());
+ }
+
+ private TFieldIdEnum findFieldId(ThriftMetadata.ThriftStruct metadata, String fieldName) {
+ Collection<ThriftMetadata.ThriftObject> fields = metadata.fields.values();
+ for (ThriftMetadata.ThriftObject field : fields) {
+ if (fieldName.equalsIgnoreCase(field.fieldId.getFieldName())) {
+ return field.fieldId;
+ }
+ }
+
+ fail("Field not found: " + fieldName);
+ return null;
+ }
+
+ private Object getFieldValue(TBase struct, TFieldIdEnum fieldId) {
+ TFieldIdEnum fieldRef = struct.fieldForId(fieldId.getThriftFieldId());
+ if (struct.isSet(fieldRef)) {
+ return struct.getFieldValue(fieldRef);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/lib/java/test/org/apache/thrift/partial/ValidateTest.java b/lib/java/test/org/apache/thrift/partial/ValidateTest.java
new file mode 100644
index 000000000..9d96844f8
--- /dev/null
+++ b/lib/java/test/org/apache/thrift/partial/ValidateTest.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.partial;
+
+import org.apache.thrift.partial.ExceptionAsserts;
+import org.apache.thrift.partial.TestData;
+
+import org.junit.Test;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class ValidateTest {
+ @Test
+ public void testCheckNotNull() {
+ String nonNullArg = "nonNullArg";
+ String nullArg = null;
+
+ // Should not throw.
+ Validate.checkNotNull(nonNullArg, "nonNullArg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'nullArg' must not be null",
+ () -> Validate.checkNotNull(nullArg, "nullArg"));
+ }
+
+ @Test
+ public void testCheckPositiveInteger() {
+ int positiveArg = 1;
+ int zero = 0;
+ int negativeArg = -1;
+
+ // Should not throw.
+ Validate.checkPositiveInteger(positiveArg, "positiveArg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'negativeArg' must be a positive integer",
+ () -> Validate.checkPositiveInteger(negativeArg, "negativeArg"));
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'zero' must be a positive integer",
+ () -> Validate.checkPositiveInteger(zero, "zero"));
+ }
+
+ @Test
+ public void testCheckNotNegative() {
+ int positiveArg = 1;
+ int zero = 0;
+ int negativeArg = -1;
+
+ // Should not throw.
+ Validate.checkNotNegative(zero, "zeroArg");
+ Validate.checkNotNegative(positiveArg, "positiveArg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'negativeArg' must not be negative",
+ () -> Validate.checkNotNegative(negativeArg, "negativeArg"));
+ }
+
+ @Test
+ public void testCheckRequired() {
+ // Should not throw.
+ Validate.checkRequired(true, "arg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' is required",
+ () -> Validate.checkRequired(false, "arg"));
+ }
+
+ @Test
+ public void testCheckValid() {
+ // Should not throw.
+ Validate.checkValid(true, "arg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' is invalid",
+ () -> Validate.checkValid(false, "arg"));
+ }
+
+ @Test
+ public void testCheckValidWithValues() {
+ String validValues = "foo, bar";
+
+ // Should not throw.
+ Validate.checkValid(true, "arg", validValues);
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' is invalid. Valid values are: foo, bar",
+ () -> Validate.checkValid(false, "arg", validValues));
+ }
+
+ @Test
+ public void testCheckNotNullAndNotEmpty() {
+ // Should not throw.
+ Validate.checkNotNullAndNotEmpty(TestData.nonEmptyArray, "array");
+ Validate.checkNotNullAndNotEmpty(TestData.nonEmptyByteArray, "array");
+ Validate.checkNotNullAndNotEmpty(TestData.nonEmptyShortArray, "array");
+ Validate.checkNotNullAndNotEmpty(TestData.nonEmptyIntArray, "array");
+ Validate.checkNotNullAndNotEmpty(TestData.nonEmptyLongArray, "array");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'string' must not be empty",
+ () -> Validate.checkNotNullAndNotEmpty("", "string"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullByteArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyByteArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullShortArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyShortArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullIntArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyIntArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullLongArray, "array"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'array' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyLongArray, "array"));
+ }
+
+ @Test
+ public void testCheckListNotNullAndNotEmpty() {
+ // Should not throw.
+ Validate.checkNotNullAndNotEmpty(TestData.validList, "list");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'list' must not be null",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.nullList, "list"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'list' must have at least one element",
+ () -> Validate.checkNotNullAndNotEmpty(TestData.emptyList, "list"));
+ }
+
+ @Test
+ public void testCheckNotNullAndNumberOfElements() {
+ // Should not throw.
+ Validate.checkNotNullAndNumberOfElements(Arrays.asList(1, 2, 3), 3, "arg");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' must not be null",
+ () -> Validate.checkNotNullAndNumberOfElements(null, 3, "arg")
+ );
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "Number of elements in 'arg' must be exactly 3, 2 given.",
+ () -> Validate.checkNotNullAndNumberOfElements(Arrays.asList(1, 2), 3, "arg")
+ );
+ }
+
+ @Test
+ public void testCheckValuesEqual() {
+ // Should not throw.
+ Validate.checkValuesEqual(1, "arg1", 1, "arg2");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg1' (1) must equal 'arg2' (2)",
+ () -> Validate.checkValuesEqual(1, "arg1", 2, "arg2"));
+ }
+
+ @Test
+ public void testCheckIntegerMultiple() {
+ // Should not throw.
+ Validate.checkIntegerMultiple(10, "arg1", 5, "arg2");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg1' (10) must be an integer multiple of 'arg2' (3)",
+ () -> Validate.checkIntegerMultiple(10, "arg1", 3, "arg2"));
+ }
+
+ @Test
+ public void testCheckGreater() {
+ // Should not throw.
+ Validate.checkGreater(10, "arg1", 5, "arg2");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg1' (5) must be greater than 'arg2' (10)",
+ () -> Validate.checkGreater(5, "arg1", 10, "arg2"));
+ }
+
+ @Test
+ public void testCheckGreaterOrEqual() {
+ // Should not throw.
+ Validate.checkGreaterOrEqual(10, "arg1", 5, "arg2");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg1' (5) must be greater than or equal to 'arg2' (10)",
+ () -> Validate.checkGreaterOrEqual(5, "arg1", 10, "arg2"));
+ }
+
+ @Test
+ public void testCheckWithinRange() {
+ // Should not throw.
+ Validate.checkWithinRange(10, "arg", 5, 15);
+ Validate.checkWithinRange(10.0, "arg", 5.0, 15.0);
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' (5) must be within the range [10, 20]",
+ () -> Validate.checkWithinRange(5, "arg", 10, 20));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'arg' (5.0) must be within the range [10.0, 20.0]",
+ () -> Validate.checkWithinRange(5.0, "arg", 10.0, 20.0));
+ }
+
+ @Test
+ public void testCheckPathExists() throws IOException {
+ Path tempFile = Files.createTempFile("foo", "bar");
+ Path tempDir = tempFile.getParent();
+ Path notFound = Paths.get("<not-found>");
+
+ // Should not throw.
+ Validate.checkPathExists(tempFile, "tempFile");
+ Validate.checkPathExists(tempDir, "tempDir");
+
+ // Verify it throws.
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "'nullArg' must not be null",
+ () -> Validate.checkPathExists(null, "nullArg"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "Path notFound (<not-found>) does not exist",
+ () -> Validate.checkPathExists(notFound, "notFound"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "must point to a directory",
+ () -> Validate.checkPathExistsAsDir(tempFile, "tempFile"));
+
+ ExceptionAsserts.assertThrows(
+ IllegalArgumentException.class,
+ "must point to a file",
+ () -> Validate.checkPathExistsAsFile(tempDir, "tempDir"));
+ }
+}