/* * 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.base; import org.apache.flink.api.common.typeutils.CompatibilityResult; import org.apache.flink.api.common.typeutils.SerializerTestInstance; import org.apache.flink.api.common.typeutils.TypeSerializerConfigSnapshot; import org.apache.flink.api.common.typeutils.TypeSerializerUtil; import org.apache.flink.core.memory.DataInputViewStreamWrapper; import org.apache.flink.core.memory.DataOutputViewStreamWrapper; import org.apache.flink.util.InstantiationUtil; import org.apache.flink.util.TestLogger; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Arrays; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class EnumSerializerTest extends TestLogger { @Test public void testPublicEnum() { testEnumSerializer(PrivateEnum.ONE, PrivateEnum.TWO, PrivateEnum.THREE); } @Test public void testPrivateEnum() { testEnumSerializer(PublicEnum.FOO, PublicEnum.BAR, PublicEnum.PETER, PublicEnum.NATHANIEL, PublicEnum.EMMA, PublicEnum.PAULA); } @Test(expected = IllegalArgumentException.class) public void testEmptyEnum() { new EnumSerializer<>(EmptyEnum.class); } @Test public void testReconfiguration() { // mock the previous ordering of enum constants to be BAR, PAULA, NATHANIEL PublicEnum[] mockPreviousOrder = {PublicEnum.BAR, PublicEnum.PAULA, PublicEnum.NATHANIEL}; // now, the actual order of FOO, BAR, PETER, NATHANIEL, EMMA, PAULA will be the "new wrong order" EnumSerializer<PublicEnum> serializer = new EnumSerializer<>(PublicEnum.class); // verify that the serializer is first using the "wrong order" (i.e., the initial new configuration) assertEquals(PublicEnum.FOO.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.FOO).intValue()); assertEquals(PublicEnum.BAR.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.BAR).intValue()); assertEquals(PublicEnum.PETER.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PETER).intValue()); assertEquals(PublicEnum.NATHANIEL.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.NATHANIEL).intValue()); assertEquals(PublicEnum.EMMA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.EMMA).intValue()); assertEquals(PublicEnum.PAULA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PAULA).intValue()); // reconfigure and verify compatibility CompatibilityResult<PublicEnum> compatResult = serializer.ensureCompatibility( new EnumSerializer.EnumSerializerConfigSnapshot<>(PublicEnum.class, mockPreviousOrder)); assertFalse(compatResult.isRequiresMigration()); // after reconfiguration, the order should be first the original BAR, PAULA, NATHANIEL, // followed by the "new enum constants" FOO, PETER, EMMA PublicEnum[] expectedOrder = {PublicEnum.BAR, PublicEnum.PAULA, PublicEnum.NATHANIEL, PublicEnum.FOO, PublicEnum.PETER, PublicEnum.EMMA}; int i = 0; for (PublicEnum constant : expectedOrder) { assertEquals(i, serializer.getValueToOrdinal().get(constant).intValue()); i++; } assertTrue(Arrays.equals(expectedOrder, serializer.getValues())); } @Test public void testConfigurationSnapshotSerialization() throws Exception { EnumSerializer<PublicEnum> serializer = new EnumSerializer<>(PublicEnum.class); byte[] serializedConfig; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { TypeSerializerUtil.writeSerializerConfigSnapshot( new DataOutputViewStreamWrapper(out), serializer.snapshotConfiguration()); serializedConfig = out.toByteArray(); } TypeSerializerConfigSnapshot restoredConfig; try (ByteArrayInputStream in = new ByteArrayInputStream(serializedConfig)) { restoredConfig = TypeSerializerUtil.readSerializerConfigSnapshot( new DataInputViewStreamWrapper(in), Thread.currentThread().getContextClassLoader()); } CompatibilityResult<PublicEnum> compatResult = serializer.ensureCompatibility(restoredConfig); assertFalse(compatResult.isRequiresMigration()); assertEquals(PublicEnum.FOO.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.FOO).intValue()); assertEquals(PublicEnum.BAR.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.BAR).intValue()); assertEquals(PublicEnum.PETER.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PETER).intValue()); assertEquals(PublicEnum.NATHANIEL.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.NATHANIEL).intValue()); assertEquals(PublicEnum.EMMA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.EMMA).intValue()); assertEquals(PublicEnum.PAULA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PAULA).intValue()); assertTrue(Arrays.equals(PublicEnum.values(), serializer.getValues())); } @Test public void testSerializeEnumSerializer() throws Exception { EnumSerializer<PublicEnum> serializer = new EnumSerializer<>(PublicEnum.class); // verify original transient parameters assertEquals(PublicEnum.FOO.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.FOO).intValue()); assertEquals(PublicEnum.BAR.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.BAR).intValue()); assertEquals(PublicEnum.PETER.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PETER).intValue()); assertEquals(PublicEnum.NATHANIEL.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.NATHANIEL).intValue()); assertEquals(PublicEnum.EMMA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.EMMA).intValue()); assertEquals(PublicEnum.PAULA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PAULA).intValue()); assertTrue(Arrays.equals(PublicEnum.values(), serializer.getValues())); byte[] serializedSerializer = InstantiationUtil.serializeObject(serializer); // deserialize and re-verify transient parameters serializer = InstantiationUtil.deserializeObject(serializedSerializer, Thread.currentThread().getContextClassLoader()); assertEquals(PublicEnum.FOO.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.FOO).intValue()); assertEquals(PublicEnum.BAR.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.BAR).intValue()); assertEquals(PublicEnum.PETER.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PETER).intValue()); assertEquals(PublicEnum.NATHANIEL.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.NATHANIEL).intValue()); assertEquals(PublicEnum.EMMA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.EMMA).intValue()); assertEquals(PublicEnum.PAULA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PAULA).intValue()); assertTrue(Arrays.equals(PublicEnum.values(), serializer.getValues())); } @Test public void testSerializeReconfiguredEnumSerializer() throws Exception { // mock the previous ordering of enum constants to be BAR, PAULA, NATHANIEL PublicEnum[] mockPreviousOrder = {PublicEnum.BAR, PublicEnum.PAULA, PublicEnum.NATHANIEL}; // now, the actual order of FOO, BAR, PETER, NATHANIEL, EMMA, PAULA will be the "new wrong order" EnumSerializer<PublicEnum> serializer = new EnumSerializer<>(PublicEnum.class); // verify that the serializer is first using the "wrong order" (i.e., the initial new configuration) assertEquals(PublicEnum.FOO.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.FOO).intValue()); assertEquals(PublicEnum.BAR.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.BAR).intValue()); assertEquals(PublicEnum.PETER.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PETER).intValue()); assertEquals(PublicEnum.NATHANIEL.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.NATHANIEL).intValue()); assertEquals(PublicEnum.EMMA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.EMMA).intValue()); assertEquals(PublicEnum.PAULA.ordinal(), serializer.getValueToOrdinal().get(PublicEnum.PAULA).intValue()); // reconfigure and verify compatibility CompatibilityResult<PublicEnum> compatResult = serializer.ensureCompatibility( new EnumSerializer.EnumSerializerConfigSnapshot<>(PublicEnum.class, mockPreviousOrder)); assertFalse(compatResult.isRequiresMigration()); // serialize and deserialize again the serializer byte[] serializedSerializer = InstantiationUtil.serializeObject(serializer); serializer = InstantiationUtil.deserializeObject(serializedSerializer, Thread.currentThread().getContextClassLoader()); // verify that after the serializer was read, the reconfigured constant ordering is untouched PublicEnum[] expectedOrder = {PublicEnum.BAR, PublicEnum.PAULA, PublicEnum.NATHANIEL, PublicEnum.FOO, PublicEnum.PETER, PublicEnum.EMMA}; int i = 0; for (PublicEnum constant : expectedOrder) { assertEquals(i, serializer.getValueToOrdinal().get(constant).intValue()); i++; } assertTrue(Arrays.equals(expectedOrder, serializer.getValues())); } @SafeVarargs public final <T extends Enum<T>> void testEnumSerializer(T... data) { @SuppressWarnings("unchecked") final Class<T> clazz = (Class<T>) data.getClass().getComponentType(); SerializerTestInstance<T> tester = new SerializerTestInstance<>( new EnumSerializer<T>(clazz), clazz, 4, data); tester.testAll(); } // ------------------------------------------------------------------------ // Test enums // ------------------------------------------------------------------------ public enum PublicEnum { FOO, BAR, PETER, NATHANIEL, EMMA, PAULA } public enum EmptyEnum {} private enum PrivateEnum { ONE, TWO, THREE } }