/* * 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.flink.api.common.typeutils; import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeutils.base.BooleanSerializer; import org.apache.flink.api.common.typeutils.base.LongSerializer; import org.apache.flink.api.common.typeutils.base.StringSerializer; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.api.java.typeutils.TypeExtractor; import org.apache.flink.core.memory.DataInputView; import org.apache.flink.core.memory.DataOutputView; import org.junit.Assert; import org.junit.Test; import javax.annotation.Nonnull; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.IntFunction; import java.util.stream.IntStream; /** Test suite for {@link CompositeSerializer}. */ public class CompositeSerializerTest { private static final ExecutionConfig execConf = new ExecutionConfig(); private static final List<Tuple2<TypeSerializer<?>, Object[]>> TEST_FIELD_SERIALIZERS = Arrays.asList( Tuple2.of(BooleanSerializer.INSTANCE, new Object[] { true, false }), Tuple2.of(LongSerializer.INSTANCE, new Object[] { 1L, 23L }), Tuple2.of(StringSerializer.INSTANCE, new Object[] { "teststr1", "teststr2" }), Tuple2.of(TypeInformation.of(Pojo.class).createSerializer(execConf), new Object[] { new Pojo(3, new String[] { "123", "456" }), new Pojo(6, new String[] { }) }) ); @Test public void testSingleFieldSerializer() { TEST_FIELD_SERIALIZERS.forEach(t -> { @SuppressWarnings("unchecked") TypeSerializer<Object>[] fieldSerializers = new TypeSerializer[] { t.f0 }; List<Object>[] instances = Arrays.stream(t.f1) .map(Arrays::asList) .toArray((IntFunction<List<Object>[]>) List[]::new); runTests(t.f0.getLength(), fieldSerializers, instances); }); } @Test public void testPairFieldSerializer() { TEST_FIELD_SERIALIZERS.forEach(t1 -> TEST_FIELD_SERIALIZERS.forEach(t2 -> { @SuppressWarnings("unchecked") TypeSerializer<Object>[] fieldSerializers = new TypeSerializer[] { t1.f0, t2.f0 }; List<Object>[] instances = IntStream.range(0, t1.f1.length) .mapToObj(i -> Arrays.asList(t1.f1[i], t2.f1[i])) .toArray((IntFunction<List<Object>[]>) List[]::new); runTests(getLength(fieldSerializers), fieldSerializers, instances); })); } @Test public void testAllFieldSerializer() { @SuppressWarnings("unchecked") TypeSerializer<Object>[] fieldSerializers = TEST_FIELD_SERIALIZERS.stream() .map(t -> (TypeSerializer<Object>) t.f0) .toArray((IntFunction<TypeSerializer<Object>[]>) TypeSerializer[]::new); List<Object>[] instances = IntStream.range(0, TEST_FIELD_SERIALIZERS.get(0).f1.length) .mapToObj(CompositeSerializerTest::getTestCase) .toArray((IntFunction<List<Object>[]>) List[]::new); runTests(getLength(fieldSerializers), fieldSerializers, instances); } // needs to be Arrays.ArrayList for all tests private static List<Object> getTestCase(int index) { return Arrays.asList(TEST_FIELD_SERIALIZERS.stream() .map(t -> t.f1[index]) .toArray(Object[]::new)); } private static int getLength(TypeSerializer<Object>[] fieldSerializers) { return Arrays.stream(fieldSerializers).allMatch(fs -> fs.getLength() > 0) ? Arrays.stream(fieldSerializers).mapToInt(TypeSerializer::getLength).sum() : -1; } @SuppressWarnings("unchecked") private void runTests( int length, TypeSerializer<Object>[] fieldSerializers, List<Object> ... instances) { try { for (boolean immutability : Arrays.asList(true, false)) { TypeSerializer<List<Object>> serializer = new TestListCompositeSerializer(immutability, fieldSerializers); CompositeSerializerTestInstance test = new CompositeSerializerTestInstance(serializer, length, instances); test.testAll(); } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); Assert.fail(e.getMessage()); } } private static class Pojo { public int f1; public String[] f2; private Pojo(int f1, String[] f2) { this.f1 = f1; this.f2 = f2; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Pojo pojo = (Pojo) o; return f1 == pojo.f1 && Arrays.equals(f2, pojo.f2); } @Override public int hashCode() { int result = Objects.hash(f1); result = 31 * result + Arrays.hashCode(f2); return result; } @Override public String toString() { return "Pojo{" + "f1=" + f1 + ", f2=" + Arrays.toString(f2) + '}'; } } private static class TestListCompositeSerializer extends CompositeSerializer<List<Object>> { TestListCompositeSerializer(boolean isImmutableTargetType, TypeSerializer<?>... fieldSerializers) { super(isImmutableTargetType, fieldSerializers); } TestListCompositeSerializer(PrecomputedParameters precomputed, TypeSerializer<?>... fieldSerializers) { super(precomputed, fieldSerializers); } @Override public List<Object> createInstance(@Nonnull Object... values) { return Arrays.asList(values); } @Override protected void setField(@Nonnull List<Object> value, int index, Object fieldValue) { if (precomputed.immutable) { throw new UnsupportedOperationException("Type is immutable"); } else { value.set(index, fieldValue); } } @Override protected Object getField(@Nonnull List<Object> value, int index) { return value.get(index); } @Override protected CompositeSerializer<List<Object>> createSerializerInstance( PrecomputedParameters precomputed, TypeSerializer<?>... originalSerializers) { return new TestListCompositeSerializer(precomputed, originalSerializers); } @Override public TypeSerializerSnapshot<List<Object>> snapshotConfiguration() { return new TestListCompositeSerializerSnapshot(this, precomputed.immutableTargetType); } } public static class TestListCompositeSerializerSnapshot extends CompositeTypeSerializerSnapshot<List<Object>, TestListCompositeSerializer> { private boolean isImmutableTargetType; /** * Constructor for read instantiation. */ public TestListCompositeSerializerSnapshot() { super(TestListCompositeSerializer.class); this.isImmutableTargetType = false; } /** * Constructor to create the snapshot for writing. */ public TestListCompositeSerializerSnapshot( TestListCompositeSerializer serializerInstance, boolean isImmutableTargetType) { super(serializerInstance); this.isImmutableTargetType = isImmutableTargetType; } @Override protected int getCurrentOuterSnapshotVersion() { return 0; } @Override protected void writeOuterSnapshot(DataOutputView out) throws IOException { out.writeBoolean(isImmutableTargetType); } @Override protected void readOuterSnapshot( int readOuterSnapshotVersion, DataInputView in, ClassLoader userCodeClassLoader) throws IOException { this.isImmutableTargetType = in.readBoolean(); } @Override protected TypeSerializer<?>[] getNestedSerializers(TestListCompositeSerializer outerSerializer) { return outerSerializer.fieldSerializers; } @Override protected TestListCompositeSerializer createOuterSerializerWithNestedSerializers(TypeSerializer<?>[] nestedSerializers) { return new TestListCompositeSerializer(isImmutableTargetType, nestedSerializers); } } private static class CompositeSerializerTestInstance extends SerializerTestInstance<List<Object>> { @SuppressWarnings("unchecked") CompositeSerializerTestInstance( TypeSerializer<List<Object>> serializer, int length, List<Object> ... testData) { super(serializer, getCls(testData[0]), length, testData); } private static Class<List<Object>> getCls(List<Object> instance) { return TypeExtractor.getForObject(instance).getTypeClass(); } } }