summaryrefslogtreecommitdiff
path: root/lib/java/test/org/apache/thrift/partial
diff options
context:
space:
mode:
Diffstat (limited to 'lib/java/test/org/apache/thrift/partial')
-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
11 files changed, 1854 insertions, 0 deletions
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"));
+ }
+}