From bb67c0d0d35b5c4f2a446b8430ba778c81fe592b Mon Sep 17 00:00:00 2001 From: Robinson Date: Mon, 17 May 2021 00:14:32 +0200 Subject: [PATCH] Added unit tests from upstream --- .../serializers/EnumMapSerializerTest.java | 87 ++ .../FieldAnnotationAwareSerializerTest.java | 143 +++ test/dorkbox/serializers/KryoTest.java | 892 ++++++++++++++++++ .../serializers/SubListSerializersTest.java | 179 ++++ test/dorkbox/serializers/TestClasses.java | 862 +++++++++++++++++ .../UnicodeBlockSerializerTest.java | 81 ++ 6 files changed, 2244 insertions(+) create mode 100644 test/dorkbox/serializers/EnumMapSerializerTest.java create mode 100644 test/dorkbox/serializers/FieldAnnotationAwareSerializerTest.java create mode 100644 test/dorkbox/serializers/KryoTest.java create mode 100644 test/dorkbox/serializers/SubListSerializersTest.java create mode 100644 test/dorkbox/serializers/TestClasses.java create mode 100644 test/dorkbox/serializers/UnicodeBlockSerializerTest.java diff --git a/test/dorkbox/serializers/EnumMapSerializerTest.java b/test/dorkbox/serializers/EnumMapSerializerTest.java new file mode 100644 index 0000000..7633358 --- /dev/null +++ b/test/dorkbox/serializers/EnumMapSerializerTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +import java.util.EnumMap; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.kryo.Kryo; + +/** + * A test case for the {@link EnumMapSerializer}. + */ +public +class EnumMapSerializerTest { + private static + enum Vipers { + SNAKE_CHARMER, BLACK_MAMBA, COTTONMOUTH, COPPERHEAD, CALIFORNIA_MOUNTAIN_SNAKE, SIDEWINDER + } + + + private static + enum Colors { + BLUE, ORANGE, PINK, WHITE, BROWN, BLONDE + } + + + private Kryo _kryo; + private EnumMap> _original; + + @Before + public + void beforeTest() { + _kryo = new Kryo(); + _kryo.register(EnumMap.class, new EnumMapSerializer()); + _original = new EnumMap>(Vipers.class); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test(expected = ClassCastException.class) + public + void testCopyEmpty() throws Exception { + EnumMap copy = _kryo.copy(_original); + // The next statement asserts that the key type of the copy is initialized correctly - + // it should throw the expected ClassCastException. + copy.put(Colors.BROWN, new HashSet()); + } + + @Test + public + void testDeepCopy() throws Exception { + _kryo.register(java.util.HashSet.class); + + final Set mambaAka = new HashSet(); + mambaAka.add("Beatrix Kiddo"); + mambaAka.add("The Bride"); + _original.put(Vipers.BLACK_MAMBA, mambaAka); + + EnumMap> copy = _kryo.copy(_original); + assertNotSame(_original, copy); + assertTrue(copy.containsKey(Vipers.BLACK_MAMBA)); + assertNotSame(_original.get(Vipers.BLACK_MAMBA), copy.get(Vipers.BLACK_MAMBA)); + assertEquals(_original, copy); + } +} diff --git a/test/dorkbox/serializers/FieldAnnotationAwareSerializerTest.java b/test/dorkbox/serializers/FieldAnnotationAwareSerializerTest.java new file mode 100644 index 0000000..35ed9f6 --- /dev/null +++ b/test/dorkbox/serializers/FieldAnnotationAwareSerializerTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Arrays; + +import org.junit.Test; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.SerializerFactory; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +/** + * A test case for the {@link FieldAnnotationAwareSerializer}. + * + * @author Rafael Winterhalter + * @author Martin Grotzke + */ +public class FieldAnnotationAwareSerializerTest { + + // Use Non-ASCII characters in order to be able to check the byte buffer for + // the existence of the string values. + protected static final String FIRST_VALUE = "åæø first value"; + protected static final String SECOND_VALUE = "äöü second value"; + + private static final int BUFFER_SIZE = 1024; + + private CustomBean makeBean() { + final CustomBean customBean = new CustomBean(); + customBean.setFirstValue(FIRST_VALUE); + customBean.setSecondValue(SECOND_VALUE); + return customBean; + } + + private byte[] makeBuffer() { + return new byte[BUFFER_SIZE]; + } + + @Test + public void testExcludeFields() throws Exception { + + final Kryo kryo = new Kryo(); + @SuppressWarnings("unchecked") + final SerializerFactory disregardingSerializerFactory = new FieldAnnotationAwareSerializer.Factory( + Arrays.>asList(CustomMark.class), true); + kryo.addDefaultSerializer(CustomBean.class, disregardingSerializerFactory); + kryo.register(CustomBean.class); + + final byte[] buffer = makeBuffer(); + + final CustomBean outputBean = makeBean(); + final Output output = new Output(buffer); + kryo.writeObject(output, outputBean); + + final Input input = new Input(buffer); + final CustomBean inputBean = kryo.readObject(input, CustomBean.class); + + assertEquals(inputBean.getSecondValue(), outputBean.getSecondValue()); + assertFalse(new String(buffer).contains(outputBean.getFirstValue())); + assertTrue(new String(buffer).contains(outputBean.getSecondValue())); + assertNull(inputBean.getFirstValue()); + } + + @Test + public void testIncludeFields() throws Exception { + + final Kryo kryo = new Kryo(); + @SuppressWarnings("unchecked") + final SerializerFactory regardingSerializerFactory = new FieldAnnotationAwareSerializer.Factory( + Arrays.>asList(CustomMark.class), false); + kryo.addDefaultSerializer(CustomBean.class, regardingSerializerFactory); + kryo.register(CustomBean.class); + + final byte[] buffer = makeBuffer(); + + final CustomBean outputBean = makeBean(); + final Output output = new Output(buffer); + kryo.writeObject(output, outputBean); + + final Input input = new Input(buffer); + final CustomBean inputBean = kryo.readObject(input, CustomBean.class); + + assertEquals(inputBean.getFirstValue(), outputBean.getFirstValue()); + assertTrue(new String(buffer).contains(outputBean.getFirstValue())); + assertFalse(new String(buffer).contains(outputBean.getSecondValue())); + assertNull(inputBean.getSecondValue()); + } + + private static class CustomBean { + + @CustomMark + private String firstValue; + + private String secondValue; + + public String getSecondValue() { + return secondValue; + } + + public void setSecondValue(final String secondValue) { + this.secondValue = secondValue; + } + + public String getFirstValue() { + return firstValue; + } + + public void setFirstValue(final String firstValue) { + this.firstValue = firstValue; + } + } + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + private static @interface CustomMark { + } +} diff --git a/test/dorkbox/serializers/KryoTest.java b/test/dorkbox/serializers/KryoTest.java new file mode 100644 index 0000000..4b937af --- /dev/null +++ b/test/dorkbox/serializers/KryoTest.java @@ -0,0 +1,892 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + +import static dorkbox.serializers.TestClasses.Person.Gender; +import static dorkbox.serializers.TestClasses.createPerson; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.net.URI; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Currency; +import java.util.Date; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Pattern; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import dorkbox.serializers.TestClasses.ClassWithoutDefaultConstructor; +import dorkbox.serializers.TestClasses.Container; +import dorkbox.serializers.TestClasses.CounterHolder; +import dorkbox.serializers.TestClasses.CounterHolderArray; +import dorkbox.serializers.TestClasses.Email; +import dorkbox.serializers.TestClasses.HashMapWithIntConstructorOnly; +import dorkbox.serializers.TestClasses.Holder; +import dorkbox.serializers.TestClasses.HolderArray; +import dorkbox.serializers.TestClasses.HolderList; +import dorkbox.serializers.TestClasses.MyContainer; +import dorkbox.serializers.TestClasses.Person; + + +/** + * Test for {@link Kryo} serialization. + * + * @author Martin Grotzke + */ +public class KryoTest { + + private Kryo _kryo; + + @Before + public void beforeTest() { + _kryo = new Kryo() { +// @Override +// public Serializer getDefaultSerializer( final Class type ) { +// if ( EnumSet.class.isAssignableFrom( type ) ) { +// return new EnumMapSerializer(); +// } +// if ( EnumMap.class.isAssignableFrom( type ) ) { +// return new EnumMapSerializer(); +// } +// return super.getDefaultSerializer( type ); +// } + }; + + + _kryo.setRegistrationRequired(false); + + SerializationDefaults.INSTANCE.register(_kryo); + } + + @Test + public void testSingletonList() throws Exception { + final List obj = Collections.singletonList( "foo" ); + final List deserialized = deserialize( serialize( obj ), obj.getClass() ); + assertDeepEquals( deserialized, obj ); + } + + @Test + public void testCopySingletonList() throws Exception { + final List obj = Collections.singletonList( "foo" ); + final List copy = _kryo.copy( obj ); + assertDeepEquals( copy, obj ); + } + + @Test + public void testSingletonSet() throws Exception { + final Set obj = Collections.singleton( "foo" ); + final Set deserialized = deserialize( serialize( obj ), obj.getClass() ); + assertDeepEquals( deserialized, obj ); + } + + @Test + public void testCopySingletonSet() throws Exception { + final Set obj = Collections.singleton( "foo" ); + final Set copy = _kryo.copy( obj ); + assertDeepEquals( copy, obj ); + } + + @Test + public void testSingletonMap() throws Exception { + final Map obj = Collections.singletonMap( "foo", "bar" ); + final Map deserialized = deserialize( serialize( obj ), obj.getClass() ); + assertDeepEquals( deserialized, obj ); + } + + @Test + public void testCopySingletonMap() throws Exception { + final Map obj = Collections.singletonMap( "foo", "bar" ); + final Map copy = _kryo.copy( obj ); + assertDeepEquals( copy, obj ); + } + + @Test + public void testEnumSet() throws Exception { + final EnumSet set = EnumSet.allOf( Gender.class ); + final EnumSet deserialized = deserialize( serialize( set ), set.getClass() ); + assertDeepEquals( deserialized, set ); + } + + @Test + public void testEmptyEnumSet() throws Exception { + final EnumSet set = EnumSet.allOf( Gender.class ); + final EnumSet deserialized = deserialize( serialize( set ), set.getClass() ); + assertDeepEquals( deserialized, set ); + } + + @Test + public void testCopyEnumSet() throws Exception { + final EnumSet set = EnumSet.allOf( Gender.class ); + final EnumSet copy = _kryo.copy(set); + assertDeepEquals( copy, set ); + } + + @Test + public void testEnumMap() throws Exception { + final EnumMap map = new EnumMap<>(Gender.class); + final String value = "foo"; + map.put( Gender.FEMALE, value ); + // Another entry with the same value - to check reference handling + map.put( Gender.MALE, value ); + @SuppressWarnings( "unchecked" ) + final EnumMap deserialized = deserialize( serialize( map ), map.getClass() ); + assertDeepEquals( deserialized, map ); + } + + @Test(expected = KryoException.class) + public void testEmptyEnumMap() throws Exception { + final EnumMap map = new EnumMap<>(Gender.class); + @SuppressWarnings( "unchecked" ) + final EnumMap deserialized = deserialize( serialize( map ), map.getClass() ); + assertDeepEquals( deserialized, map ); + } + + @Test + public void testCopyEnumMap() throws Exception { + final EnumMap map = new EnumMap<>(Gender.class); + final String value = "foo"; + map.put( Gender.FEMALE, value ); + final EnumMap copy = _kryo.copy(map); + assertDeepEquals( copy, map ); + } + + /** + * test that insertion order is retained. + */ + @Test + public void testCopyForIterateMapSerializer() throws Exception { + final Map map = new LinkedHashMap<>(); + // use doubles as e.g. integers hash to the value... + for( int i = 0; i < 10; i++ ) { + map.put(Double.valueOf(i + "." + Math.abs(i ) ), "value: " + i ); + } + @SuppressWarnings( "unchecked" ) + final Map deserialized = deserialize( serialize( map ), map.getClass() ); + assertDeepEquals( deserialized, map ); + } + + @Test + public void testGregorianCalendar() throws Exception { + final Holder cal = new Holder<>(Calendar.getInstance(Locale.ENGLISH)); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( cal ), Holder.class ); + assertDeepEquals( deserialized, cal ); + + assertEquals( deserialized.item.getTimeInMillis(), cal.item.getTimeInMillis() ); + assertEquals( deserialized.item.getTimeZone(), cal.item.getTimeZone() ); + assertEquals( deserialized.item.getMinimalDaysInFirstWeek(), cal.item.getMinimalDaysInFirstWeek() ); + assertEquals( deserialized.item.getFirstDayOfWeek(), cal.item.getFirstDayOfWeek() ); + assertEquals( deserialized.item.isLenient(), cal.item.isLenient() ); + } + + @Test + public void testCopyGregorianCalendar() throws Exception { + final Holder cal = new Holder<>(Calendar.getInstance(Locale.ENGLISH)); + final Holder copy = _kryo.copy( cal ); + assertDeepEquals( copy, cal ); + + assertEquals( copy.item.getTimeInMillis(), cal.item.getTimeInMillis() ); + assertEquals( copy.item.getTimeZone(), cal.item.getTimeZone() ); + assertEquals( copy.item.getMinimalDaysInFirstWeek(), cal.item.getMinimalDaysInFirstWeek() ); + assertEquals( copy.item.getFirstDayOfWeek(), cal.item.getFirstDayOfWeek() ); + assertEquals( copy.item.isLenient(), cal.item.isLenient() ); + } + + @Test + public void testJavaUtilDate() throws Exception { + final Holder cal = new Holder<>(new Date(System.currentTimeMillis())); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( cal ), Holder.class ); + assertDeepEquals( deserialized, cal ); + assertEquals(deserialized.item.getTime(), cal.item.getTime()); + } + + @Test + public void testCopyJavaUtilDate() throws Exception { + final Holder cal = new Holder<>(new Date(System.currentTimeMillis())); + final Holder copy = _kryo.copy( cal ); + assertDeepEquals( copy, cal ); + assertEquals(copy.item.getTime(), cal.item.getTime()); + } + + @Test + public void testJavaSqlTimestamp() throws Exception { + final Holder cal = new Holder<>(new Timestamp(System.currentTimeMillis())); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( cal ), Holder.class ); + assertDeepEquals( deserialized, cal ); + assertEquals( deserialized.item.getTime(), cal.item.getTime() ); + } + + @Test + public void testCopyJavaSqlTimestamp() throws Exception { + final Holder cal = new Holder<>(new Timestamp(System.currentTimeMillis())); + final Holder copy = _kryo.copy( cal ); + assertDeepEquals( copy, cal ); + assertEquals( copy.item.getTime(), cal.item.getTime() ); + } + + @Test + public void testJavaSqlDate() throws Exception { + final Holder date = new Holder<>(new java.sql.Date(System.currentTimeMillis())); + @SuppressWarnings("unchecked") + final Holder deserialized = deserialize(serialize(date), Holder.class); + assertDeepEquals(deserialized, date); + assertEquals(deserialized.item.getTime(), date.item.getTime()); + } + + @Test + public void testCopyJavaSqlDate() throws Exception { + final Holder date = new Holder<>(new java.sql.Date(System.currentTimeMillis())); + final Holder copy = _kryo.copy(date); + assertDeepEquals(copy, date); + assertEquals(copy.item.getTime(), date.item.getTime()); + } + + @Test + public void testJavaSqlTime() throws Exception { + final Holder time = new Holder<>(new java.sql.Time(System.currentTimeMillis())); + @SuppressWarnings("unchecked") + final Holder deserialized = deserialize(serialize(time), Holder.class); + assertDeepEquals(deserialized, time); + assertEquals(deserialized.item.getTime(), time.item.getTime()); + } + + @Test + public void testCopyJavaSqlTime() throws Exception { + final Holder time = new Holder<>(new java.sql.Time(System.currentTimeMillis())); + final Holder copy = _kryo.copy(time); + assertDeepEquals(copy, time); + assertEquals(copy.item.getTime(), time.item.getTime()); + } + + @Test + public void testBitSet() throws Exception { + final BitSet bitSet = new BitSet(10); + bitSet.flip(2); + bitSet.flip(4); + final Holder holder = new Holder<>(bitSet); + @SuppressWarnings("unchecked") + final Holder deserialized = deserialize(serialize(holder), Holder.class); + assertDeepEquals(deserialized, holder); + } + + @Test + public void testCopyBitSet() throws Exception { + final BitSet bitSet = new BitSet(10); + bitSet.flip(2); + bitSet.flip(4); + final BitSet copy = _kryo.copy(bitSet); + assertDeepEquals(copy, bitSet); + } + + @Test + public void testURI() throws Exception { + final Holder uri = new Holder<>(new URI("http://www.google.com")); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( uri ), Holder.class ); + assertDeepEquals(deserialized, uri); + } + + @Test + public void testCopyURI() throws Exception { + final Holder uri = new Holder<>(new URI("http://www.google.com")); + final Holder copy = _kryo.copy( uri ); + assertDeepEquals(copy, uri); + } + + @Test + public void testUUID() throws Exception { + final Holder uuid = new Holder<>(UUID.randomUUID()); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( uuid ), Holder.class ); + assertDeepEquals( deserialized, uuid ); + } + + @Test + public void testCopyUUID() throws Exception { + final Holder uuid = new Holder<>(UUID.randomUUID()); + final Holder copy = _kryo.copy( uuid ); + assertDeepEquals( copy, uuid ); + } + + @Test + public void testRegex() throws Exception { + final Holder pattern = new Holder<>(Pattern.compile("regex")); + @SuppressWarnings( "unchecked" ) + final Holder deserialized = deserialize( serialize( pattern ), Holder.class ); + assertDeepEquals( deserialized, pattern ); + + final Holder patternWithFlags = new Holder<>(Pattern.compile("\n", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE)); + @SuppressWarnings( "unchecked" ) + final Holder deserializedWithFlags = deserialize( serialize( patternWithFlags ), Holder.class ); + assertDeepEquals( deserializedWithFlags, patternWithFlags ); + } + + @Test + public void testCopyRegex() throws Exception { + final Holder pattern = new Holder<>(Pattern.compile("regex")); + final Holder copy = _kryo.copy( pattern ); + assertDeepEquals( copy, pattern ); + + final Holder patternWithFlags = new Holder<>(Pattern.compile("\n", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE)); + final Holder copyWithFlags = _kryo.copy( patternWithFlags ); + assertDeepEquals( copyWithFlags, patternWithFlags ); + } + + @Test + public void testStringBuffer() throws Exception { + final StringBuffer stringBuffer = new StringBuffer( "with some content \n& some lines..." ); + final StringBuffer deserialized = deserialize( serialize( stringBuffer ), StringBuffer.class ); + assertDeepEquals( deserialized, stringBuffer ); + } + + @Test + public void testStringBuilder() throws Exception { + final StringBuilder stringBuilder = new StringBuilder( "with some content \n& some lines..." ); + final StringBuilder deserialized = deserialize( serialize( stringBuilder ), StringBuilder.class ); + assertDeepEquals( deserialized, stringBuilder ); + } + + @Test + public void testMapWithIntConstructorOnly() throws Exception { + final HashMapWithIntConstructorOnly map = new HashMapWithIntConstructorOnly(5 ); + final HashMapWithIntConstructorOnly deserialized = deserialize( serialize( map ), HashMapWithIntConstructorOnly.class ); + assertDeepEquals( deserialized, map ); + } + + @Test + public void testCurrency() throws Exception { + final Currency currency = Currency.getInstance( "EUR" ); + final Currency deserialized = deserialize( serialize( currency ), Currency.class ); + assertDeepEquals( deserialized, currency ); + + // Check that the transient field defaultFractionDigits is initialized correctly + Assert.assertEquals( deserialized.getCurrencyCode(), currency.getCurrencyCode() ); + Assert.assertEquals( deserialized.getDefaultFractionDigits(), currency.getDefaultFractionDigits() ); + } + + public Object[][] unmodifiableCollections() { + final HashMap m = new HashMap<>(); + m.put( "foo", "bar" ); + return new Object[][] { + { Collections.unmodifiableList(new ArrayList<>(Arrays.asList("foo", "bar")) ) }, + { Collections.unmodifiableSet(new HashSet<>(Arrays.asList("foo", "bar")) ) }, + { Collections.unmodifiableMap( m ) }, + }; + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testUnmodifiableCollections() throws Exception { + final Object collection = unmodifiableCollections(); + + final Holder holder = new Holder<>(collection); + final Holder deserialized = deserialize( serialize( holder ), Holder.class ); + assertDeepEquals( deserialized, holder ); + } + + @Test + public void testCopyUnmodifiableCollections() throws Exception { + final Object collection = unmodifiableCollections(); + + final Holder unmodifiableCollection = new Holder<>(collection); + final Holder copy = _kryo.copy( unmodifiableCollection ); + assertDeepEquals( copy, unmodifiableCollection ); + } + + public Object[][] synchronizedCollections() { + final HashMap m = new HashMap<>(); + m.put( "foo", "bar" ); + return new Object[][] { + { Collections.synchronizedList(new ArrayList<>(Arrays.asList("foo", "bar")) ) }, + { Collections.synchronizedSet(new HashSet<>(Arrays.asList("foo", "bar")) ) }, + { Collections.synchronizedMap( m ) }, + }; + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testSynchronizedCollections( ) throws Exception { + final Object collection = synchronizedCollections(); + final Holder holder = new Holder<>(collection); + final Holder deserialized = deserialize( serialize( holder ), Holder.class ); + assertDeepEquals( deserialized, holder ); + } + + @Test + public void testCopySynchronizedCollections() throws Exception { + final Object collection = synchronizedCollections(); + final Holder synchronizedCollection = new Holder<>(collection); + final Holder copy = _kryo.copy( synchronizedCollection ); + assertDeepEquals( copy, synchronizedCollection ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilCollectionsEmptyList() throws Exception { + final Holder> emptyList = new Holder<>(Collections.emptyList()); + final Holder> deserialized = deserialize( serialize( emptyList ), Holder.class ); + assertDeepEquals( deserialized, emptyList ); + } + + @Test + public void testCopyJavaUtilCollectionsEmptyList() throws Exception { + final Holder> emptyList = new Holder<>(Collections.emptyList()); + final Holder> copy = _kryo.copy( emptyList ); + assertDeepEquals( copy, emptyList ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilCollectionsEmptySet() throws Exception { + final Holder> emptyList = new Holder<>(Collections.emptySet()); + final Holder> deserialized = deserialize( serialize( emptyList ), Holder.class ); + assertDeepEquals( deserialized, emptyList ); + } + + @Test + public void testCopyJavaUtilCollectionsEmptySet() throws Exception { + final Holder> emptyList = new Holder<>(Collections.emptySet()); + final Holder> copy = _kryo.copy( emptyList ); + assertDeepEquals( copy, emptyList ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilCollectionsEmptyMap() throws Exception { + final Holder> emptyMap = new Holder<>(Collections.emptyMap()); + final Holder> deserialized = deserialize( serialize( emptyMap ), Holder.class ); + assertDeepEquals( deserialized, emptyMap ); + } + + @Test + public void testCopyJavaUtilCollectionsEmptyMap() throws Exception { + final Holder> emptyMap = new Holder<>(Collections.emptyMap()); + final Holder> copy = _kryo.copy( emptyMap ); + assertDeepEquals( copy, emptyMap ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilArraysAsListEmpty() throws Exception { + final Holder> asListHolder = new Holder<>(Arrays.asList()); + final Holder> deserialized = deserialize( serialize( asListHolder ), Holder.class ); + assertDeepEquals( deserialized, asListHolder ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilArraysAsListPrimitiveArrayElement() throws Exception { + final int[] values = { 1, 2 }; + @SuppressWarnings("rawtypes") + final Holder> asListHolder = new Holder( Arrays.asList( values ) ); + final Holder> deserialized = deserialize( serialize( asListHolder ), Holder.class ); + assertDeepEquals( deserialized, asListHolder ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilArraysAsListBoxedPrimitives() throws Exception { + final Integer[] values = { 1, 2 }; + final List list = Arrays.asList(values); + final Holder> asListHolder = new Holder(list); + final Holder> deserialized = deserialize( serialize( asListHolder ), Holder.class ); + assertDeepEquals( deserialized, asListHolder ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilArraysAsListString() throws Exception { + final Holder> asListHolder = new Holder<>(Arrays.asList("foo", "bar")); + final Holder> deserialized = deserialize( serialize( asListHolder ), Holder.class ); + assertDeepEquals( deserialized, asListHolder ); + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testJavaUtilArraysAsListEmail() throws Exception { + final Holder> asListHolder = new Holder<>(Arrays.asList(new Email("foo", "foo@example.org"))); + final Holder> deserialized = deserialize( serialize( asListHolder ), Holder.class ); + assertDeepEquals( deserialized, asListHolder ); + } + + @Test + public void testCopyJavaUtilArraysAsList() throws Exception { + final List list = Arrays.asList("foo", "bar"); + final List copy = _kryo.copy(list); + assertDeepEquals( copy, list ); + } + + @Test + public void testClassSerializer() throws Exception { + final Holder> clazz = new Holder<>(String.class); + @SuppressWarnings( "unchecked" ) + final Holder> deserialized = deserialize( serialize( clazz ), Holder.class ); + assertDeepEquals( deserialized, clazz ); + } + + @Test(expected = KryoException.class) + public void testInnerClass() throws Exception { + // seems to be related to #15 + final Container container = TestClasses.createContainer(); + final Container deserialized = deserialize( serialize( container ), Container.class ); + assertDeepEquals( deserialized, container ); + } + + @Test + public void testSharedObjectIdentity_CounterHolder() throws Exception { + final AtomicInteger sharedObject = new AtomicInteger( 42 ); + final CounterHolder holder1 = new CounterHolder(sharedObject ); + final CounterHolder holder2 = new CounterHolder( sharedObject ); + final CounterHolderArray holderHolder = new CounterHolderArray(holder1, holder2 ); + + _kryo.setReferences(true); + final CounterHolderArray deserialized = deserialize( serialize( holderHolder ), CounterHolderArray.class ); + assertDeepEquals( deserialized, holderHolder ); + assertTrue( deserialized.holders[0].item == deserialized.holders[1].item ); + } + + protected Object[][] createSharedObjectIdentityProviderData() { + return new Object[][] { + { AtomicInteger.class.getSimpleName(), new AtomicInteger( 42 ) }, + { Email.class.getSimpleName(), new Email( "foo bar", "foo.bar@example.com" ) } }; + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testSharedObjectIdentityWithArray() throws Exception { + Object[][] data = createSharedObjectIdentityProviderData(); + for (final Object[] datum : data) { + String name = (String)datum[0]; + T sharedObject = (T)datum[1]; + + final Holder holder1 = new Holder<>(sharedObject); + final Holder holder2 = new Holder<>(sharedObject); + final HolderArray holderHolder = new HolderArray<>(holder1, holder2); + + _kryo.setReferences(true); + + final HolderArray deserialized = deserialize( serialize( holderHolder ), HolderArray.class ); + assertDeepEquals( deserialized, holderHolder ); + assertTrue( deserialized.holders[0].item == deserialized.holders[1].item ); + } + } + + @SuppressWarnings( "unchecked" ) + @Test + public void testSharedObjectIdentity() throws Exception { + Object[][] data = createSharedObjectIdentityProviderData(); + for (final Object[] datum : data) { + String name = (String) datum[0]; + T sharedObject = (T) datum[1]; + + final Holder holder1 = new Holder<>(sharedObject); + final Holder holder2 = new Holder<>(sharedObject); + final HolderList holderHolder = new HolderList<>(new ArrayList<>(Arrays.asList(holder1, holder2))); + + _kryo.setReferences(true); + + final HolderList deserialized = deserialize( serialize( holderHolder ), HolderList.class ); + assertDeepEquals( deserialized, holderHolder ); + assertTrue( deserialized.holders.get( 0 ).item == deserialized.holders.get( 1 ).item ); + } + } + + protected Object[][] createTypesAsSessionAttributesData() { + return new Object[][] { + { Boolean.class, Boolean.TRUE }, + { String.class, "42" }, + { StringBuilder.class, new StringBuilder( "42" ) }, + { StringBuffer.class, new StringBuffer( "42" ) }, + { Class.class, String.class }, + { Long.class, new Long( 42 ) }, + { Integer.class, new Integer( 42 ) }, + { Character.class, new Character( 'c' ) }, + { Byte.class, new Byte( "b".getBytes()[0] ) }, + { Double.class, new Double( 42d ) }, + { Float.class, new Float( 42f ) }, + { Short.class, new Short( (short) 42 ) }, + { BigDecimal.class, new BigDecimal( 42 ) }, + { AtomicInteger.class, new AtomicInteger( 42 ) }, + { AtomicLong.class, new AtomicLong( 42 ) }, + { Integer[].class, new Integer[] { 42 } }, + { Date.class, new Date( System.currentTimeMillis() - 10000 ) }, + { Calendar.class, Calendar.getInstance() }, + { Currency.class, Currency.getInstance( "EUR" ) }, + { ArrayList.class, new ArrayList<>(Arrays.asList("foo")) }, + { int[].class, new int[] { 1, 2 } }, + { long[].class, new long[] { 1, 2 } }, + { short[].class, new short[] { 1, 2 } }, + { float[].class, new float[] { 1, 2 } }, + { double[].class, new double[] { 1, 2 } }, + { int[].class, new int[] { 1, 2 } }, + { byte[].class, "42".getBytes() }, + { char[].class, "42".toCharArray() }, + { String[].class, new String[] { "23", "42" } }, + {Person[].class, new Person[] {createPerson("foo bar", Gender.MALE, 42 ) } } }; + } + + @Test + public void testTypesAsSessionAttributes() throws Exception { + Object[][] data = createTypesAsSessionAttributesData(); + for (final Object[] datum : data) { + final Class type = (Class) datum[0]; + final T instance = (T) datum[1]; + + @SuppressWarnings( "unchecked" ) + final T deserialized = (T) deserialize( serialize( instance ), instance.getClass() ); + assertDeepEquals( deserialized, instance ); + } + } + + @Test + public void testTypesInContainerClass() throws Exception { + final MyContainer myContainer = new MyContainer(); + final MyContainer deserialized = deserialize( serialize( myContainer ), MyContainer.class ); + assertDeepEquals( deserialized, myContainer ); + } + + @Test(expected = KryoException.class) + public void testClassWithoutDefaultConstructor() throws Exception { + final ClassWithoutDefaultConstructor obj = TestClasses.createClassWithoutDefaultConstructor("foo" ); + final ClassWithoutDefaultConstructor deserialized = deserialize( serialize( obj ), ClassWithoutDefaultConstructor.class ); + assertDeepEquals( deserialized, obj ); + } + + @Test + public void testPrivateClass() throws Exception { + final Holder holder = new Holder( TestClasses.createPrivateClass( "foo" ) ); + final Holder deserialized = deserialize( serialize( holder ), Holder.class ); + assertDeepEquals( deserialized, holder ); + } + + @Test + public void testCollections() throws Exception { + final EntityWithCollections obj = new EntityWithCollections(); + final EntityWithCollections deserialized = deserialize( serialize( obj ), EntityWithCollections.class ); + assertDeepEquals( deserialized, obj ); + } + + @Test + public void testCyclicDependencies() throws Exception { + _kryo.setReferences(true); + + final Person p1 = createPerson( "foo bar", Gender.MALE, 42, "foo.bar@example.org", "foo.bar@example.com" ); + final Person p2 = createPerson( "bar baz", Gender.FEMALE, 42, "bar.baz@example.org", "bar.baz@example.com" ); + p1.addFriend( p2 ); + p2.addFriend( p1 ); + + final Person deserialized = deserialize( serialize( p1 ), Person.class ); + assertDeepEquals( deserialized, p1 ); + } + + @SuppressWarnings("RedundantIfStatement") + public static class EntityWithCollections { + private final String[] _bars; + private final List _foos; + private final Map _bazens; + + public EntityWithCollections() { + _bars = new String[] { "foo", "bar" }; + _foos = new ArrayList<>(Arrays.asList("foo", "bar")); + _bazens = new HashMap<>(); + _bazens.put( "foo", 1 ); + _bazens.put( "bar", 2 ); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode( _bars ); + result = prime * result + ( ( _bazens == null ) + ? 0 + : _bazens.hashCode() ); + result = prime * result + ( ( _foos == null ) + ? 0 + : _foos.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final EntityWithCollections other = (EntityWithCollections) obj; + if ( !Arrays.equals( _bars, other._bars ) ) { + return false; + } + if ( _bazens == null ) { + if ( other._bazens != null ) { + return false; + } + } else if ( !_bazens.equals( other._bazens ) ) { + return false; + } + if ( _foos == null ) { + if ( other._foos != null ) { + return false; + } + } else if ( !_foos.equals( other._foos ) ) { + return false; + } + return true; + } + } + + public static void assertDeepEquals( final Object one, final Object another ) throws Exception { + assertDeepEquals( one, another, new IdentityHashMap<>() ); + } + + private static void assertDeepEquals( final Object one, final Object another, final Map alreadyChecked ) + throws Exception { + if ( one == another ) { + return; + } + if ( one == null && another != null || one != null && another == null ) { + Assert.fail("One of both is null: " + one + ", " + another ); + } + if ( alreadyChecked.containsKey( one ) ) { + return; + } + alreadyChecked.put( one, another ); + + Assert.assertEquals( one.getClass(), another.getClass() ); + if ( one.getClass().isPrimitive() || one instanceof String || one instanceof Character || one instanceof Boolean + || one instanceof Class ) { + Assert.assertEquals( one, another ); + return; + } + + if ( Map.class.isAssignableFrom( one.getClass() ) ) { + final Map m1 = (Map) one; + final Map m2 = (Map) another; + Assert.assertEquals( m1.size(), m2.size() ); + final Iterator> iter1 = m1.entrySet().iterator(); + while( iter1.hasNext() ) { + Map.Entry entry1 = iter1.next(); + assertDeepEquals(entry1.getValue(), m2.get(entry1.getKey()), alreadyChecked ); + } + return; + } + + if ( Number.class.isAssignableFrom( one.getClass() ) ) { + Assert.assertEquals( ( (Number) one ).longValue(), ( (Number) another ).longValue() ); + return; + } + + if ( one instanceof Currency ) { + // Check that the transient field defaultFractionDigits is initialized correctly (that was issue #34) + final Currency currency1 = ( Currency) one; + final Currency currency2 = ( Currency) another; + Assert.assertEquals( currency1.getCurrencyCode(), currency2.getCurrencyCode() ); + Assert.assertEquals( currency1.getDefaultFractionDigits(), currency2.getDefaultFractionDigits() ); + } + + if (Collection.class.isAssignableFrom(one.getClass())) { + final Collection c1 = (Collection) one; + final Collection c2 = (Collection) another; + Assert.assertEquals( c1.size(), c2.size() ); + + final Iterator iter1 = c1.iterator(); + final Iterator iter2 = c2.iterator(); + while( iter1.hasNext() ) { + assertDeepEquals(iter1.next(), iter2.next(), alreadyChecked); + } + + Assert.assertFalse(iter2.hasNext()); + return; + } + + Class clazz = one.getClass(); + while ( clazz != null ) { + assertEqualDeclaredFields( clazz, one, another, alreadyChecked ); + clazz = clazz.getSuperclass(); + } + } + + private static void assertEqualDeclaredFields( final Class clazz, final Object one, final Object another, + final Map alreadyChecked ) throws Exception, IllegalAccessException { + for ( final Field field : clazz.getDeclaredFields() ) { + field.setAccessible( true ); + if ( !Modifier.isTransient( field.getModifiers() ) ) { + assertDeepEquals( field.get( one ), field.get( another ), alreadyChecked ); + } + } + } + + protected byte[] serialize( final Object o ) { + return serialize(_kryo, o); + } + + public static byte[] serialize(final Kryo kryo, final Object o) { + if ( o == null ) { + throw new NullPointerException( "Can't serialize null" ); + } + + final Output output = new Output(4096); + kryo.writeObject(output, o); + output.flush(); + return output.getBuffer(); + } + + protected T deserialize( final byte[] in, final Class clazz ) { + return deserialize(_kryo, in, clazz); + } + + public static T deserialize(final Kryo kryo, final byte[] in, final Class clazz) { + final Input input = new Input(in); + return kryo.readObject(input, clazz); + } +} diff --git a/test/dorkbox/serializers/SubListSerializersTest.java b/test/dorkbox/serializers/SubListSerializersTest.java new file mode 100644 index 0000000..8dd4fde --- /dev/null +++ b/test/dorkbox/serializers/SubListSerializersTest.java @@ -0,0 +1,179 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + +import static dorkbox.serializers.KryoTest.deserialize; +import static dorkbox.serializers.KryoTest.serialize; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.objenesis.strategy.StdInstantiatorStrategy; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; + +/** + * Test for {@link SubListSerializers}. + * + * @author Martin Grotzke + */ +public class SubListSerializersTest { + + private Kryo _kryo; + + @Before + public void beforeClass() { + _kryo = new Kryo(); + + _kryo.setRegistrationRequired(false); + + SubListSerializers.addDefaultSerializers(_kryo); + + final DefaultInstantiatorStrategy instantiatorStrategy = new DefaultInstantiatorStrategy(); + instantiatorStrategy.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); + _kryo.setInstantiatorStrategy(instantiatorStrategy); + } + + private void doTest(final List subList) { + final byte[] serialized = serialize( _kryo, subList ); + @SuppressWarnings( "unchecked" ) + final List deserialized = deserialize( _kryo, serialized, subList.getClass() ); + + assertEquals( deserialized, subList ); + assertEquals( deserialized.remove( 0 ), subList.remove( 0 ) ); + } + + private void doTestCopy(final List subList) { + final List copy = _kryo.copy( subList ); + + assertEquals( copy, subList ); + assertEquals( copy.remove( 0 ), subList.remove( 0 ) ); + } + + @Test + public void testSubList () throws Exception { + final List subList = new LinkedList( Arrays.asList( TestEnum.values() ) ).subList( 1, 2 ); + doTest(subList); + } + + @Test + public void testCopySubList () throws Exception { + final List subList = new LinkedList( Arrays.asList( TestEnum.values() ) ).subList( 1, 2 ); + doTestCopy(subList); + } + + @Test + public void testSubListSubList () throws Exception { + final List subList = new LinkedList( Arrays.asList( TestEnum.values() ) ).subList( 1, 3 ).subList(1, 2); + doTest(subList); + } + + @Test + public void testCopySubListSubList () throws Exception { + final List subList = new LinkedList( Arrays.asList( TestEnum.values() ) ).subList( 1, 3 ).subList(1, 2); + doTestCopy(subList); + } + + @Test + public void testArrayListSubList () throws Exception { + final List subList = new ArrayList( Arrays.asList( TestEnum.values() ) ).subList( 1, 2 ); + doTest(subList); + } + + @Test + public void testCopyArrayListSubList () throws Exception { + final List subList = new ArrayList( Arrays.asList( TestEnum.values() ) ).subList( 1, 2 ); + doTestCopy(subList); + } + + @Test + public void testArrayListSubListSubList () throws Exception { + final List subList = new ArrayList( Arrays.asList( TestEnum.values() ) ).subList( 1, 3 ).subList(1, 2); + doTest(subList); + } + + @Test + public void testCopyArrayListSubListSubList () throws Exception { + final List subList = new ArrayList( Arrays.asList( TestEnum.values() ) ).subList( 1, 3 ).subList(1, 2); + doTestCopy(subList); + } + + @Test + public void testArrayListSubListWithSharedItems () throws Exception { + final List mylist = arrayList("1", "1", "2", "1", "1"); + final List subList = mylist.subList(0, 5); + + final byte[] serialized = serialize( _kryo, subList ); + @SuppressWarnings( "unchecked" ) + final List deserialized = deserialize( _kryo, serialized, subList.getClass() ); + + assertEquals( deserialized, subList ); + assertEquals( deserialized, mylist ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void testNestedArrayListSubListWithSharedItems_1() throws Exception { + final List l1 = arrayList("1", "1", "2"); + final List l1s1 = l1.subList(0, 3); + + final List l1s2 = l1.subList(1, 3); + + final List l2 = arrayList("1", "2", "3"); + final List l2s1 = l2.subList(0, 3); + + final List> lists = new ArrayList>(Arrays.asList(l1s1, l1s2, l2s1, l1, l2)); + + final byte[] serialized = serialize( _kryo, lists ); + final List> deserialized = deserialize( _kryo, serialized, lists.getClass() ); + + assertEquals( deserialized, lists ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void testNestedArrayListSubListWithSharedItems_2() throws Exception { + final List l1 = arrayList("1", "1", "2"); + final List l1s1 = l1.subList(0, 3); + + final List l1s2 = l1.subList(1, 3); + + final List l2 = arrayList("1", "2", "3"); + final List l2s1 = l2.subList(0, 3); + + final List> lists = new ArrayList>(Arrays.asList(l1, l2, l1s1, l1s2, l2s1)); + + final byte[] serialized = serialize( _kryo, lists ); + final List> deserialized = deserialize( _kryo, serialized, lists.getClass() ); + + assertEquals( deserialized, lists ); + } + + static enum TestEnum { + ITEM1, ITEM2, ITEM3 + } + + private static ArrayList arrayList(final T ... items) { + return new ArrayList(Arrays.asList(items)); + } + +} diff --git a/test/dorkbox/serializers/TestClasses.java b/test/dorkbox/serializers/TestClasses.java new file mode 100644 index 0000000..0c0a0f6 --- /dev/null +++ b/test/dorkbox/serializers/TestClasses.java @@ -0,0 +1,862 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + +import static dorkbox.serializers.TestClasses.Person.*; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Currency; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + + +/** + * @author Martin Grotzke + */ +public class TestClasses { + + public static Person createPerson( final String name, final Gender gender, final String... emailAddresses ) { + final Person person = new Person(); + person.setName( name ); + person.setGender( gender ); + if ( emailAddresses != null ) { + final HashMap props = new HashMap(); + for ( int i = 0; i < emailAddresses.length; i++ ) { + final String emailAddress = emailAddresses[i]; + props.put( "email" + i, new Email( name, emailAddress ) ); + } + person.setProps( props ); + } + return person; + } + + public static Person createPerson( final String name, final Gender gender, final Integer age, final String... emailAddresses ) { + final Person person = new Person(); + person.setName( name ); + person.setGender( gender ); + person.setAge( age ); + final HashMap props = new HashMap(); + for ( int i = 0; i < emailAddresses.length; i++ ) { + final String emailAddress = emailAddresses[i]; + props.put( "email" + i, new Email( name, emailAddress ) ); + } + person.setProps( props ); + return person; + } + + static ClassWithoutDefaultConstructor createClassWithoutDefaultConstructor( final String string ) { + return new ClassWithoutDefaultConstructor( string ); + } + + static PrivateClass createPrivateClass( final String string ) { + final PrivateClass result = new PrivateClass(); + result.foo = string; + return result; + } + + static Container createContainer() { + return new Container(); + } + + static SomeInterface createProxy() { + return (SomeInterface) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), + new Class[] { SomeInterface.class, Serializable.class }, + new MyInvocationHandler( SomeInterfaceImpl.class ) ); + } + + static class MyInvocationHandler implements InvocationHandler { + + private final Class _targetClazz; + private transient Object _target; + + public MyInvocationHandler( final Class targetClazz ) { + _targetClazz = targetClazz; + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args ) throws Throwable { + if ( _target == null ) { + _target = _targetClazz.newInstance(); + } + return method.invoke( _target, args ); + } + } + + static interface SomeInterface { + String hello(); + } + + static class SomeInterfaceImpl implements SomeInterface { + + /** + * {@inheritDoc} + */ + @Override + public String hello() { + return "hi"; + } + + } + + public static class Container { + + @SuppressWarnings( "unused" ) + private final Body _body; + + public Container() { + _body = new Body(); + } + + class Body { + } + + } + + @SuppressWarnings("ConstantConditions") + public static class Person implements Serializable { + + private static final long serialVersionUID = 1L; + + public enum Gender { + MALE, + FEMALE + } + + private String _name; + private Gender _gender; + private Integer _age; + private Map _props; + private final Collection _friends = new ArrayList(); + + public String getName() { + return _name; + } + + public void addFriend( final Person p ) { + _friends.add( p ); + } + + public void setName( final String name ) { + _name = name; + } + + public Map getProps() { + return _props; + } + + public void setProps( final Map props ) { + _props = props; + } + + public Gender getGender() { + return _gender; + } + + public void setGender( final Gender gender ) { + _gender = gender; + } + + public Integer getAge() { + return _age; + } + + public void setAge( final Integer age ) { + _age = age; + } + + public Collection getFriends() { + return _friends; + } + + /** + * @param friends + * @param friends2 + * @return + */ + private boolean flatEquals( final Collection c1, final Collection c2 ) { + return c1 == c2 || c1 != null && c2 != null && c1.size() == c2.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( _age == null ) + ? 0 + : _age.hashCode() ); + result = prime * result + ( ( _friends == null ) + ? 0 + : _friends.size() ); + result = prime * result + ( ( _gender == null ) + ? 0 + : _gender.hashCode() ); + result = prime * result + ( ( _name == null ) + ? 0 + : _name.hashCode() ); + result = prime * result + ( ( _props == null ) + ? 0 + : _props.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final Person other = (Person) obj; + if ( _age == null ) { + if ( other._age != null ) { + return false; + } + } else if ( !_age.equals( other._age ) ) { + return false; + } + if ( _friends == null ) { + if ( other._friends != null ) { + return false; + } + } else if ( !flatEquals( _friends, other._friends ) ) { + return false; + } + if ( _gender == null ) { + if ( other._gender != null ) { + return false; + } + } else if ( !_gender.equals( other._gender ) ) { + return false; + } + if ( _name == null ) { + if ( other._name != null ) { + return false; + } + } else if ( !_name.equals( other._name ) ) { + return false; + } + if ( _props == null ) { + if ( other._props != null ) { + return false; + } + } else if ( !_props.equals( other._props ) ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Person [_age=" + _age + ", _friends.size=" + _friends.size() + ", _gender=" + _gender + ", _name=" + _name + + ", _props=" + _props + "]"; + } + + } + + @SuppressWarnings("RedundantIfStatement") + public static class Email implements Serializable { + + private static final long serialVersionUID = 1L; + + private String _name; + private String _email; + + public Email() { + } + + public Email( final String name, final String email ) { + super(); + _name = name; + _email = email; + } + + public String getName() { + return _name; + } + + public void setName( final String name ) { + _name = name; + } + + public String getEmail() { + return _email; + } + + public void setEmail( final String email ) { + _email = email; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( _email == null ) + ? 0 + : _email.hashCode() ); + result = prime * result + ( ( _name == null ) + ? 0 + : _name.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final Email other = (Email) obj; + if ( _email == null ) { + if ( other._email != null ) { + return false; + } + } else if ( !_email.equals( other._email ) ) { + return false; + } + if ( _name == null ) { + if ( other._name != null ) { + return false; + } + } else if ( !_name.equals( other._name ) ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Email [_email=" + _email + ", _name=" + _name + "]"; + } + + } + + @SuppressWarnings("RedundantIfStatement") + public static class PublicClass { + PrivateClass privateClass; + + public PublicClass() { + } + + public PublicClass( final PrivateClass protectedClass ) { + this.privateClass = protectedClass; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( privateClass == null ) + ? 0 + : privateClass.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final PublicClass other = (PublicClass) obj; + if ( privateClass == null ) { + if ( other.privateClass != null ) { + return false; + } + } else if ( !privateClass.equals( other.privateClass ) ) { + return false; + } + return true; + } + } + + @SuppressWarnings("RedundantIfStatement") + private static class PrivateClass { + String foo; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( foo == null ) + ? 0 + : foo.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final PrivateClass other = (PrivateClass) obj; + if ( foo == null ) { + if ( other.foo != null ) { + return false; + } + } else if ( !foo.equals( other.foo ) ) { + return false; + } + return true; + } + } + + @SuppressWarnings("RedundantIfStatement") + public static class ClassWithoutDefaultConstructor { + final String value; + + public ClassWithoutDefaultConstructor( final String value ) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( value == null ) + ? 0 + : value.hashCode() ); + return result; + } + + @Override + public boolean equals( final Object obj ) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( getClass() != obj.getClass() ) { + return false; + } + final ClassWithoutDefaultConstructor other = (ClassWithoutDefaultConstructor) obj; + if ( value == null ) { + if ( other.value != null ) { + return false; + } + } else if ( !value.equals( other.value ) ) { + return false; + } + return true; + } + + @Override + public String toString() { + return "ClassWithoutDefaultConstructor [value=" + value + "]"; + } + } + + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + public static class MyContainer { + private int _int; + private long _long; + private final boolean _boolean; + private final Boolean _Boolean; + private final Class _Class; + private String _String; + private final StringBuilder _StringBuilder; + private final StringBuffer _StringBuffer; + private Long _Long; + private Integer _Integer; + private Character _Character; + private Byte _Byte; + private Double _Double; + private Float _Float; + private Short _Short; + private BigDecimal _BigDecimal; + private AtomicInteger _AtomicInteger; + private AtomicLong _AtomicLong; + private Integer[] _IntegerArray; + private Date _Date; + private Calendar _Calendar; + private final Currency _Currency; + private List _ArrayList; + private final Set _HashSet; + private final Map _HashMap; + private int[] _intArray; + private long[] _longArray; + private short[] _shortArray; + private float[] _floatArray; + private double[] _doubleArray; + private byte[] _byteArray; + private char[] _charArray; + private String[] _StringArray; + private Person[] _PersonArray; + + public MyContainer() { + + _int = 1; + _long = 2; + _boolean = true; + _Boolean = Boolean.TRUE; + _Class = String.class; + _String = "3"; + _StringBuffer = new StringBuffer( "foo" ); + _StringBuilder = new StringBuilder( "foo" ); + _Long = new Long( 4 ); + _Integer = new Integer( 5 ); + _Character = new Character( 'c' ); + _Byte = new Byte( "b".getBytes()[0] ); + _Double = new Double( 6d ); + _Float = new Float( 7f ); + _Short = new Short( (short) 8 ); + _BigDecimal = new BigDecimal( 9 ); + _AtomicInteger = new AtomicInteger( 10 ); + _AtomicLong = new AtomicLong( 11 ); + _IntegerArray = new Integer[] { 13 }; + _Date = new Date( System.currentTimeMillis() - 10000 ); + _Calendar = Calendar.getInstance(); + _Currency = Currency.getInstance( "EUR" ); + _ArrayList = new ArrayList( Arrays.asList( "foo" ) ); + _HashSet = new HashSet(); + _HashSet.add( "14" ); + + _HashMap = new HashMap(); + _HashMap.put( "foo", 23 ); + _HashMap.put( "bar", 42 ); + + _intArray = new int[] { 1, 2 }; + _longArray = new long[] { 1, 2 }; + _shortArray = new short[] { 1, 2 }; + _floatArray = new float[] { 1, 2 }; + _doubleArray = new double[] { 1, 2 }; + _byteArray = "42".getBytes(); + _charArray = "42".toCharArray(); + _StringArray = new String[] { "23", "42" }; + _PersonArray = new Person[] { createPerson("foo bar", Gender.MALE, 42 ) }; + + } + + public int getInt() { + return _int; + } + + public void setInt( final int i ) { + _int = i; + } + + public long getLong() { + return _long; + } + + public void setLong( final long l ) { + _long = l; + } + + public String getString() { + return _String; + } + + public void setString( final String string ) { + _String = string; + } + + public Long getLongWrapper() { + return _Long; + } + + public void setLongWrapper( final Long l ) { + _Long = l; + } + + public Integer getInteger() { + return _Integer; + } + + public void setInteger( final Integer integer ) { + _Integer = integer; + } + + public Character getCharacter() { + return _Character; + } + + public void setCharacter( final Character character ) { + _Character = character; + } + + public Byte getByte() { + return _Byte; + } + + public void setByte( final Byte b ) { + _Byte = b; + } + + public Double getDouble() { + return _Double; + } + + public void setDouble( final Double d ) { + _Double = d; + } + + public Float getFloat() { + return _Float; + } + + public void setFloat( final Float f ) { + _Float = f; + } + + public Short getShort() { + return _Short; + } + + public void setShort( final Short s ) { + _Short = s; + } + + public BigDecimal getBigDecimal() { + return _BigDecimal; + } + + public void setBigDecimal( final BigDecimal bigDecimal ) { + _BigDecimal = bigDecimal; + } + + public AtomicInteger getAtomicInteger() { + return _AtomicInteger; + } + + public void setAtomicInteger( final AtomicInteger atomicInteger ) { + _AtomicInteger = atomicInteger; + } + + public AtomicLong getAtomicLong() { + return _AtomicLong; + } + + public void setAtomicLong( final AtomicLong atomicLong ) { + _AtomicLong = atomicLong; + } + + public Integer[] getIntegerArray() { + return _IntegerArray; + } + + public void setIntegerArray( final Integer[] integerArray ) { + _IntegerArray = integerArray; + } + + public Date getDate() { + return _Date; + } + + public void setDate( final Date date ) { + _Date = date; + } + + public Calendar getCalendar() { + return _Calendar; + } + + public void setCalendar( final Calendar calendar ) { + _Calendar = calendar; + } + + public List getArrayList() { + return _ArrayList; + } + + public void setArrayList( final List arrayList ) { + _ArrayList = arrayList; + } + + public int[] getIntArray() { + return _intArray; + } + + public void setIntArray( final int[] intArray ) { + _intArray = intArray; + } + + public long[] getLongArray() { + return _longArray; + } + + public void setLongArray( final long[] longArray ) { + _longArray = longArray; + } + + public short[] getShortArray() { + return _shortArray; + } + + public void setShortArray( final short[] shortArray ) { + _shortArray = shortArray; + } + + public float[] getFloatArray() { + return _floatArray; + } + + public void setFloatArray( final float[] floatArray ) { + _floatArray = floatArray; + } + + public double[] getDoubleArray() { + return _doubleArray; + } + + public void setDoubleArray( final double[] doubleArray ) { + _doubleArray = doubleArray; + } + + public byte[] getByteArray() { + return _byteArray; + } + + public void setByteArray( final byte[] byteArray ) { + _byteArray = byteArray; + } + + public char[] getCharArray() { + return _charArray; + } + + public void setCharArray( final char[] charArray ) { + _charArray = charArray; + } + + public String[] getStringArray() { + return _StringArray; + } + + public void setStringArray( final String[] stringArray ) { + _StringArray = stringArray; + } + + public Person[] getPersonArray() { + return _PersonArray; + } + + public void setPersonArray( final Person[] personArray ) { + _PersonArray = personArray; + } + + public Set getHashSet() { + return _HashSet; + } + + public Map getHashMap() { + return _HashMap; + } + + } + + static class Holder { + T item; + + /** + * Default constructor, added for kryo... + */ + public Holder() { + } + + public Holder( final T item ) { + this.item = item; + } + } + + static class HolderList { + List> holders; + + public HolderList( ) { } + + public HolderList( final List> holders ) { + this.holders = holders; + } + } + + static class CounterHolder { + AtomicInteger item; + + public CounterHolder() { } + + public CounterHolder( final AtomicInteger item ) { + this.item = item; + } + } + + static class CounterHolderArray { + CounterHolder[] holders; + + public CounterHolderArray( ) { } + + public CounterHolderArray( final CounterHolder... holders ) { + this.holders = holders; + } + } + + static class HolderArray { + Holder[] holders; + + public HolderArray() {} + public HolderArray( final Holder... holders ) { + this.holders = holders; + } + } + + public static class HashMapWithIntConstructorOnly extends HashMap { + + private static final long serialVersionUID = 1L; + + @SuppressWarnings( "unused" ) + private HashMapWithIntConstructorOnly() { + } + + public HashMapWithIntConstructorOnly( final int size ) { + super( size ); + } + + } + +} diff --git a/test/dorkbox/serializers/UnicodeBlockSerializerTest.java b/test/dorkbox/serializers/UnicodeBlockSerializerTest.java new file mode 100644 index 0000000..656240e --- /dev/null +++ b/test/dorkbox/serializers/UnicodeBlockSerializerTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2021 dorkbox, llc + * + * Licensed 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 dorkbox.serializers; + +import static dorkbox.serializers.KryoTest.deserialize; +import static dorkbox.serializers.KryoTest.serialize; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.lang.Character.UnicodeBlock; + +import org.junit.Before; +import org.junit.Test; +import org.objenesis.ObjenesisStd; +import org.objenesis.strategy.StdInstantiatorStrategy; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; + + +/** + * Test for {@link UnicodeBlockSerializer}. + * + * @author Chris Hennick + */ +public class UnicodeBlockSerializerTest { + + private static final String NONEXISTENT_BLOCK_NAME = "RURITANIAN"; + private Kryo kryo; + + private static class ThingWithUnicodeBlock { + final UnicodeBlock unicodeBlock; + + private ThingWithUnicodeBlock(UnicodeBlock unicodeBlock) { + this.unicodeBlock = unicodeBlock; + } + } + + @Before + public void beforeTest() { + kryo = new Kryo(); + final DefaultInstantiatorStrategy instantiatorStrategy = new DefaultInstantiatorStrategy(); + instantiatorStrategy.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); + kryo.setInstantiatorStrategy(instantiatorStrategy); + kryo.register(ThingWithUnicodeBlock.class); + kryo.register(UnicodeBlock.class, new UnicodeBlockSerializer()); + } + + @Test + public void testBasicRoundTrip() { + byte[] serialized = serialize(kryo, UnicodeBlock.UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS); + assertSame(deserialize(kryo, serialized, UnicodeBlock.class), + UnicodeBlock.UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS); + } + + @Test + public void testDeserializingUnknownInstanceReturnsNull() { + byte[] serialized = serialize(kryo, new ObjenesisStd().newInstance(UnicodeBlock.class)); + assertNull(deserialize(kryo, serialized, UnicodeBlock.class)); + } + + @Test + public void testCopyContainingObject() { + ThingWithUnicodeBlock original = new ThingWithUnicodeBlock(UnicodeBlock.GREEK); + assertSame(kryo.copy(original).unicodeBlock, UnicodeBlock.GREEK); + } +}