/* * Copyright 2010 Martin Grotzke * * 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 de.javakaffee.kryoserializers; import static de.javakaffee.kryoserializers.TestClasses.createPerson; import static org.testng.Assert.assertEquals; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; 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.GregorianCalendar; 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.apache.commons.lang.mutable.MutableInt; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.DefaultSerializers.BigDecimalSerializer; import com.esotericsoftware.kryo.serializers.DefaultSerializers.BigIntegerSerializer; import de.javakaffee.kryoserializers.TestClasses.ClassWithoutDefaultConstructor; import de.javakaffee.kryoserializers.TestClasses.Container; import de.javakaffee.kryoserializers.TestClasses.CounterHolder; import de.javakaffee.kryoserializers.TestClasses.CounterHolderArray; import de.javakaffee.kryoserializers.TestClasses.Email; import de.javakaffee.kryoserializers.TestClasses.HashMapWithIntConstructorOnly; import de.javakaffee.kryoserializers.TestClasses.Holder; import de.javakaffee.kryoserializers.TestClasses.HolderArray; import de.javakaffee.kryoserializers.TestClasses.HolderList; import de.javakaffee.kryoserializers.TestClasses.MyContainer; import de.javakaffee.kryoserializers.TestClasses.Person; import de.javakaffee.kryoserializers.TestClasses.Person.Gender; import de.javakaffee.kryoserializers.TestClasses.SomeInterface; /** * Test for {@link Kryo} serialization. * * @author <a href="mailto:martin.grotzke@javakaffee.de">Martin Grotzke</a> */ public class KryoTest { private Kryo _kryo; @BeforeTest protected void beforeTest() { _kryo = new KryoReflectionFactorySupport() { @Override @SuppressWarnings( { "rawtypes", "unchecked" } ) public Serializer<?> getDefaultSerializer( final Class type ) { if ( EnumSet.class.isAssignableFrom( type ) ) { return new EnumSetSerializer(); } if ( EnumMap.class.isAssignableFrom( type ) ) { return new EnumMapSerializer(); } if ( Collection.class.isAssignableFrom( type ) ) { return new CopyForIterateCollectionSerializer(); } if ( Map.class.isAssignableFrom( type ) ) { return new CopyForIterateMapSerializer(); } if ( Date.class.isAssignableFrom( type ) ) { return new DateSerializer( type ); } return super.getDefaultSerializer( type ); } }; _kryo.setRegistrationRequired(false); _kryo.register( Arrays.asList( "" ).getClass(), new ArraysAsListSerializer() ); _kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() ); _kryo.register( Collections.EMPTY_MAP.getClass(), new CollectionsEmptyMapSerializer() ); _kryo.register( Collections.EMPTY_SET.getClass(), new CollectionsEmptySetSerializer() ); _kryo.register( Collections.singletonList( "" ).getClass(), new CollectionsSingletonListSerializer() ); _kryo.register( Collections.singleton( "" ).getClass(), new CollectionsSingletonSetSerializer() ); _kryo.register( Collections.singletonMap( "", "" ).getClass(), new CollectionsSingletonMapSerializer() ); _kryo.register( BigDecimal.class, new BigDecimalSerializer() ); _kryo.register( BigInteger.class, new BigIntegerSerializer() ); _kryo.register( Pattern.class, new RegexSerializer() ); _kryo.register( BitSet.class, new BitSetSerializer() ); _kryo.register( URI.class, new URISerializer() ); _kryo.register( UUID.class, new UUIDSerializer() ); _kryo.register( GregorianCalendar.class, new GregorianCalendarSerializer() ); _kryo.register( InvocationHandler.class, new JdkProxySerializer() ); UnmodifiableCollectionsSerializer.registerSerializers( _kryo ); SynchronizedCollectionsSerializer.registerSerializers( _kryo ); } @Test( enabled = true ) public void testSingletonList() throws Exception { final List<?> obj = Collections.singletonList( "foo" ); final List<?> deserialized = deserialize( serialize( obj ), obj.getClass() ); assertDeepEquals( deserialized, obj ); } @Test( enabled = true ) public void testCopySingletonList() throws Exception { final List<?> obj = Collections.singletonList( "foo" ); final List<?> copy = _kryo.copy( obj ); assertDeepEquals( copy, obj ); } @Test( enabled = true ) public void testSingletonSet() throws Exception { final Set<?> obj = Collections.singleton( "foo" ); final Set<?> deserialized = deserialize( serialize( obj ), obj.getClass() ); assertDeepEquals( deserialized, obj ); } @Test( enabled = true ) public void testCopySingletonSet() throws Exception { final Set<?> obj = Collections.singleton( "foo" ); final Set<?> copy = _kryo.copy( obj ); assertDeepEquals( copy, obj ); } @Test( enabled = true ) public void testSingletonMap() throws Exception { final Map<?, ?> obj = Collections.singletonMap( "foo", "bar" ); final Map<?, ?> deserialized = deserialize( serialize( obj ), obj.getClass() ); assertDeepEquals( deserialized, obj ); } @Test( enabled = true ) public void testCopySingletonMap() throws Exception { final Map<?, ?> obj = Collections.singletonMap( "foo", "bar" ); final Map<?, ?> copy = _kryo.copy( obj ); assertDeepEquals( copy, obj ); } @Test( enabled = true ) 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 testCopyEnumSet() throws Exception { final EnumSet<?> set = EnumSet.allOf( Gender.class ); final EnumSet<?> copy = _kryo.copy(set); assertDeepEquals( copy, set ); } @Test( enabled = true ) public void testEnumMap() throws Exception { final EnumMap<Gender, String> map = new EnumMap<Gender, String>( 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<Gender, String> deserialized = deserialize( serialize( map ), map.getClass() ); assertDeepEquals( deserialized, map ); } @Test public void testCopyEnumMap() throws Exception { final EnumMap<Gender, String> map = new EnumMap<Gender, String>( Gender.class ); final String value = "foo"; map.put( Gender.FEMALE, value ); final EnumMap<Gender,String> copy = _kryo.copy(map); assertDeepEquals( copy, map ); } /** * Test that linked hash map is serialized correctly with the {@link CopyForIterateMapSerializer}: * test that insertion order is retained. * @throws Exception */ @Test( enabled = true ) public void testCopyForIterateMapSerializer() throws Exception { final Map<Double, String> map = new LinkedHashMap<Double, String>(); // use doubles as e.g. integers hash to the value... for( int i = 0; i < 10; i++ ) { map.put( Double.valueOf( String.valueOf( i ) + "." + Math.abs( i ) ), "value: " + i ); } @SuppressWarnings( "unchecked" ) final Map<Double, String> deserialized = deserialize( serialize( map ), map.getClass() ); assertDeepEquals( deserialized, map ); } @Test( enabled = true ) public void testGregorianCalendar() throws Exception { final Holder<Calendar> cal = new Holder<Calendar>( Calendar.getInstance( Locale.ENGLISH ) ); @SuppressWarnings( "unchecked" ) final Holder<Calendar> 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( enabled = true ) public void testCopyGregorianCalendar() throws Exception { final Holder<Calendar> cal = new Holder<Calendar>( Calendar.getInstance( Locale.ENGLISH ) ); final Holder<Calendar> 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( enabled = true ) public void testJavaUtilDate() throws Exception { final Holder<Date> cal = new Holder<Date>( new Date(System.currentTimeMillis()) ); @SuppressWarnings( "unchecked" ) final Holder<Date> deserialized = deserialize( serialize( cal ), Holder.class ); assertDeepEquals( deserialized, cal ); assertEquals(deserialized.item.getTime(), cal.item.getTime()); } @Test( enabled = true ) public void testCopyJavaUtilDate() throws Exception { final Holder<Date> cal = new Holder<Date>( new Date(System.currentTimeMillis()) ); final Holder<Date> copy = _kryo.copy( cal ); assertDeepEquals( copy, cal ); assertEquals(copy.item.getTime(), cal.item.getTime()); } @Test( enabled = true ) public void testJavaSqlTimestamp() throws Exception { final Holder<Timestamp> cal = new Holder<Timestamp>( new Timestamp(System.currentTimeMillis()) ); @SuppressWarnings( "unchecked" ) final Holder<Timestamp> deserialized = deserialize( serialize( cal ), Holder.class ); assertDeepEquals( deserialized, cal ); assertEquals( deserialized.item.getTime(), cal.item.getTime() ); } @Test( enabled = true ) public void testCopyJavaSqlTimestamp() throws Exception { final Holder<Timestamp> cal = new Holder<Timestamp>( new Timestamp(System.currentTimeMillis()) ); final Holder<Timestamp> copy = _kryo.copy( cal ); assertDeepEquals( copy, cal ); assertEquals( copy.item.getTime(), cal.item.getTime() ); } @Test(enabled = true) public void testJavaSqlDate() throws Exception { final Holder<java.sql.Date> date = new Holder<java.sql.Date>(new java.sql.Date(System.currentTimeMillis())); @SuppressWarnings("unchecked") final Holder<java.sql.Date> deserialized = deserialize(serialize(date), Holder.class); assertDeepEquals(deserialized, date); assertEquals(deserialized.item.getTime(), date.item.getTime()); } @Test(enabled = true) public void testCopyJavaSqlDate() throws Exception { final Holder<java.sql.Date> date = new Holder<java.sql.Date>(new java.sql.Date(System.currentTimeMillis())); final Holder<java.sql.Date> copy = _kryo.copy(date); assertDeepEquals(copy, date); assertEquals(copy.item.getTime(), date.item.getTime()); } @Test(enabled = true) public void testJavaSqlTime() throws Exception { final Holder<java.sql.Time> time = new Holder<java.sql.Time>(new java.sql.Time(System.currentTimeMillis())); @SuppressWarnings("unchecked") final Holder<java.sql.Time> deserialized = deserialize(serialize(time), Holder.class); assertDeepEquals(deserialized, time); assertEquals(deserialized.item.getTime(), time.item.getTime()); } @Test(enabled = true) public void testCopyJavaSqlTime() throws Exception { final Holder<java.sql.Time> time = new Holder<java.sql.Time>(new java.sql.Time(System.currentTimeMillis())); final Holder<java.sql.Time> copy = _kryo.copy(time); assertDeepEquals(copy, time); assertEquals(copy.item.getTime(), time.item.getTime()); } @Test(enabled = true) public void testBitSet() throws Exception { final BitSet bitSet = new BitSet(10); bitSet.flip(2); bitSet.flip(4); final Holder<BitSet> holder = new Holder<BitSet>(bitSet); @SuppressWarnings("unchecked") final Holder<BitSet> deserialized = deserialize(serialize(holder), Holder.class); assertDeepEquals(deserialized, holder); } @Test(enabled = true) 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( enabled = true ) public void testURI() throws Exception { final Holder<URI> uri = new Holder<URI>( new URI("http://www.google.com") ); @SuppressWarnings( "unchecked" ) final Holder<URI> deserialized = deserialize( serialize( uri ), Holder.class ); assertDeepEquals(deserialized, uri); } @Test( enabled = true ) public void testCopyURI() throws Exception { final Holder<URI> uri = new Holder<URI>( new URI("http://www.google.com") ); final Holder<URI> copy = _kryo.copy( uri ); assertDeepEquals(copy, uri); } @Test( enabled = true ) public void testUUID() throws Exception { final Holder<UUID> uuid = new Holder<UUID>( UUID.randomUUID() ); @SuppressWarnings( "unchecked" ) final Holder<UUID> deserialized = deserialize( serialize( uuid ), Holder.class ); assertDeepEquals( deserialized, uuid ); } @Test( enabled = true ) public void testCopyUUID() throws Exception { final Holder<UUID> uuid = new Holder<UUID>( UUID.randomUUID() ); final Holder<UUID> copy = _kryo.copy( uuid ); assertDeepEquals( copy, uuid ); } @Test( enabled = true ) public void testRegex() throws Exception { final Holder<Pattern> pattern = new Holder<Pattern>( Pattern.compile("regex") ); @SuppressWarnings( "unchecked" ) final Holder<Pattern> deserialized = deserialize( serialize( pattern ), Holder.class ); assertDeepEquals( deserialized, pattern ); final Holder<Pattern> patternWithFlags = new Holder<Pattern>( Pattern.compile("\n", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE) ); @SuppressWarnings( "unchecked" ) final Holder<Pattern> deserializedWithFlags = deserialize( serialize( patternWithFlags ), Holder.class ); assertDeepEquals( deserializedWithFlags, patternWithFlags ); } @Test( enabled = true ) public void testCopyRegex() throws Exception { final Holder<Pattern> pattern = new Holder<Pattern>( Pattern.compile("regex") ); final Holder<Pattern> copy = _kryo.copy( pattern ); assertDeepEquals( copy, pattern ); final Holder<Pattern> patternWithFlags = new Holder<Pattern>( Pattern.compile("\n", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE) ); final Holder<Pattern> copyWithFlags = _kryo.copy( patternWithFlags ); assertDeepEquals( copyWithFlags, patternWithFlags ); } @Test( enabled = true ) public void testStringBuffer() throws Exception { final StringBuffer stringBuffer = new StringBuffer( "<stringbuffer>with some content \n& some lines...</stringbuffer>" ); final StringBuffer deserialized = deserialize( serialize( stringBuffer ), StringBuffer.class ); assertDeepEquals( deserialized, stringBuffer ); } @Test( enabled = true ) public void testStringBuilder() throws Exception { final StringBuilder stringBuilder = new StringBuilder( "<stringbuilder>with some content \n& some lines...</stringbuilder>" ); final StringBuilder deserialized = deserialize( serialize( stringBuilder ), StringBuilder.class ); assertDeepEquals( deserialized, stringBuilder ); } @Test( enabled = true ) public void testMapWithIntConstructorOnly() throws Exception { final HashMapWithIntConstructorOnly map = new HashMapWithIntConstructorOnly( 5 ); final HashMapWithIntConstructorOnly deserialized = deserialize( serialize( map ), HashMapWithIntConstructorOnly.class ); assertDeepEquals( deserialized, map ); } @Test( enabled = true ) 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() ); } @DataProvider public Object[][] unmodifiableCollections() { final HashMap<String, String> m = new HashMap<String, String>(); m.put( "foo", "bar" ); return new Object[][] { { Collections.unmodifiableList( new ArrayList<String>( Arrays.asList( "foo", "bar" ) ) ) }, { Collections.unmodifiableSet( new HashSet<String>( Arrays.asList( "foo", "bar" ) ) ) }, { Collections.unmodifiableMap( m ) }, }; } @SuppressWarnings( "unchecked" ) @Test( enabled = true, dataProvider = "unmodifiableCollections" ) public void testUnmodifiableCollections( final Object collection ) throws Exception { final Holder<Object> holder = new Holder<Object>( collection ); final Holder<Object> deserialized = deserialize( serialize( holder ), Holder.class ); assertDeepEquals( deserialized, holder ); } @Test( enabled = true, dataProvider = "unmodifiableCollections" ) public void testCopyUnmodifiableCollections( final Object collection ) throws Exception { final Holder<Object> unmodifiableCollection = new Holder<Object>( collection ); final Holder<Object> copy = _kryo.copy( unmodifiableCollection ); assertDeepEquals( copy, unmodifiableCollection ); } @DataProvider public Object[][] synchronizedCollections() { final HashMap<String, String> m = new HashMap<String, String>(); m.put( "foo", "bar" ); return new Object[][] { { Collections.synchronizedList( new ArrayList<String>( Arrays.asList( "foo", "bar" ) ) ) }, { Collections.synchronizedSet( new HashSet<String>( Arrays.asList( "foo", "bar" ) ) ) }, { Collections.synchronizedMap( m ) }, }; } @SuppressWarnings( "unchecked" ) @Test( enabled = true, dataProvider = "synchronizedCollections" ) public void testSynchronizedCollections( final Object collection ) throws Exception { final Holder<Object> holder = new Holder<Object>( collection ); final Holder<Object> deserialized = deserialize( serialize( holder ), Holder.class ); assertDeepEquals( deserialized, holder ); } @Test( enabled = true, dataProvider = "synchronizedCollections" ) public void testCopySynchronizedCollections( final Object collection ) throws Exception { final Holder<Object> synchronizedCollection = new Holder<Object>( collection ); final Holder<Object> copy = _kryo.copy( synchronizedCollection ); assertDeepEquals( copy, synchronizedCollection ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilCollectionsEmptyList() throws Exception { final Holder<List<String>> emptyList = new Holder<List<String>>( Collections.<String>emptyList() ); final Holder<List<String>> deserialized = deserialize( serialize( emptyList ), Holder.class ); assertDeepEquals( deserialized, emptyList ); } @Test( enabled = true ) public void testCopyJavaUtilCollectionsEmptyList() throws Exception { final Holder<List<String>> emptyList = new Holder<List<String>>( Collections.<String>emptyList() ); final Holder<List<String>> copy = _kryo.copy( emptyList ); assertDeepEquals( copy, emptyList ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilCollectionsEmptySet() throws Exception { final Holder<Set<String>> emptyList = new Holder<Set<String>>( Collections.<String>emptySet() ); final Holder<Set<String>> deserialized = deserialize( serialize( emptyList ), Holder.class ); assertDeepEquals( deserialized, emptyList ); } @Test( enabled = true ) public void testCopyJavaUtilCollectionsEmptySet() throws Exception { final Holder<Set<String>> emptyList = new Holder<Set<String>>( Collections.<String>emptySet() ); final Holder<Set<String>> copy = _kryo.copy( emptyList ); assertDeepEquals( copy, emptyList ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilCollectionsEmptyMap() throws Exception { final Holder<Map<String, String>> emptyMap = new Holder<Map<String, String>>( Collections.<String, String>emptyMap() ); final Holder<Map<String, String>> deserialized = deserialize( serialize( emptyMap ), Holder.class ); assertDeepEquals( deserialized, emptyMap ); } @Test( enabled = true ) public void testCopyJavaUtilCollectionsEmptyMap() throws Exception { final Holder<Map<String, String>> emptyMap = new Holder<Map<String, String>>( Collections.<String, String>emptyMap() ); final Holder<Map<String, String>> copy = _kryo.copy( emptyMap ); assertDeepEquals( copy, emptyMap ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilArraysAsListEmpty() throws Exception { final Holder<List<String>> asListHolder = new Holder<List<String>>( Arrays.<String> asList() ); final Holder<List<String>> deserialized = deserialize( serialize( asListHolder ), Holder.class ); assertDeepEquals( deserialized, asListHolder ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilArraysAsListPrimitiveArrayElement() throws Exception { final int[] values = { 1, 2 }; @SuppressWarnings("rawtypes") final Holder<List<String>> asListHolder = new Holder( Arrays.asList( values ) ); final Holder<List<String>> deserialized = deserialize( serialize( asListHolder ), Holder.class ); assertDeepEquals( deserialized, asListHolder ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilArraysAsListBoxedPrimitives() throws Exception { final Integer[] values = { 1, 2 }; final List<Integer> list = Arrays.asList(values); @SuppressWarnings("rawtypes") final Holder<List<Integer>> asListHolder = new Holder(list); final Holder<List<Integer>> deserialized = deserialize( serialize( asListHolder ), Holder.class ); assertDeepEquals( deserialized, asListHolder ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilArraysAsListString() throws Exception { final Holder<List<String>> asListHolder = new Holder<List<String>>( Arrays.<String> asList( "foo", "bar" ) ); final Holder<List<String>> deserialized = deserialize( serialize( asListHolder ), Holder.class ); assertDeepEquals( deserialized, asListHolder ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true ) public void testJavaUtilArraysAsListEmail() throws Exception { final Holder<List<Email>> asListHolder = new Holder<List<Email>>( Arrays.asList( new Email( "foo", "foo@example.org" ) ) ); final Holder<List<Email>> deserialized = deserialize( serialize( asListHolder ), Holder.class ); assertDeepEquals( deserialized, asListHolder ); } @Test( enabled = true ) public void testCopyJavaUtilArraysAsList() throws Exception { final List<String> list = Arrays.<String> asList("foo", "bar"); final List<String> copy = _kryo.copy(list); assertDeepEquals( copy, list ); } @Test( enabled = true ) public void testJdkProxy() throws Exception { final Holder<SomeInterface> bean = new Holder<SomeInterface>( TestClasses.createProxy() ); @SuppressWarnings( "unchecked" ) final Holder<SomeInterface> deserialized = deserialize( serialize( bean ), Holder.class ); assertDeepEquals( deserialized, bean ); } @Test( enabled = true ) public void testCopyJdkProxy() throws Exception { final Holder<SomeInterface> bean = new Holder<SomeInterface>( TestClasses.createProxy() ); final Holder<SomeInterface> copy = _kryo.copy( bean ); assertDeepEquals( copy, bean ); } @Test( enabled = true ) public void testClassSerializer() throws Exception { final Holder<Class<?>> clazz = new Holder<Class<?>>( String.class ); @SuppressWarnings( "unchecked" ) final Holder<Class<?>> deserialized = deserialize( serialize( clazz ), Holder.class ); assertDeepEquals( deserialized, clazz ); } @Test( enabled = true ) 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( enabled = true ) public <T> 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 ); final CounterHolderArray deserialized = deserialize( serialize( holderHolder ), CounterHolderArray.class ); assertDeepEquals( deserialized, holderHolder ); Assert.assertTrue( deserialized.holders[0].item == deserialized.holders[1].item ); } @DataProvider( name = "sharedObjectIdentityProvider" ) 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( enabled = true, dataProvider = "sharedObjectIdentityProvider" ) public <T> void testSharedObjectIdentityWithArray( final String name, final T sharedObject ) throws Exception { final Holder<T> holder1 = new Holder<T>( sharedObject ); final Holder<T> holder2 = new Holder<T>( sharedObject ); final HolderArray<T> holderHolder = new HolderArray<T>( holder1, holder2 ); final HolderArray<T> deserialized = deserialize( serialize( holderHolder ), HolderArray.class ); assertDeepEquals( deserialized, holderHolder ); Assert.assertTrue( deserialized.holders[0].item == deserialized.holders[1].item ); } @SuppressWarnings( "unchecked" ) @Test( enabled = true, dataProvider = "sharedObjectIdentityProvider" ) public <T> void testSharedObjectIdentity( final String name, final T sharedObject ) throws Exception { final Holder<T> holder1 = new Holder<T>( sharedObject ); final Holder<T> holder2 = new Holder<T>( sharedObject ); final HolderList<T> holderHolder = new HolderList<T>( new ArrayList<Holder<T>>( Arrays.asList( holder1, holder2 ) ) ); final HolderList<T> deserialized = deserialize( serialize( holderHolder ), HolderList.class ); assertDeepEquals( deserialized, holderHolder ); Assert.assertTrue( deserialized.holders.get( 0 ).item == deserialized.holders.get( 1 ).item ); } @DataProvider( name = "typesAsSessionAttributesProvider" ) 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 ) }, { MutableInt.class, new MutableInt( 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<String>( 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( enabled = true, dataProvider = "typesAsSessionAttributesProvider" ) public <T> void testTypesAsSessionAttributes( final Class<T> type, final T instance ) throws Exception { @SuppressWarnings( "unchecked" ) final T deserialized = (T) deserialize( serialize( instance ), instance.getClass() ); assertDeepEquals( deserialized, instance ); } @Test( enabled = true ) public void testTypesInContainerClass() throws Exception { final MyContainer myContainer = new MyContainer(); final MyContainer deserialized = deserialize( serialize( myContainer ), MyContainer.class ); assertDeepEquals( deserialized, myContainer ); } @Test( enabled = true ) public void testClassWithoutDefaultConstructor() throws Exception { final ClassWithoutDefaultConstructor obj = TestClasses.createClassWithoutDefaultConstructor( "foo" ); final ClassWithoutDefaultConstructor deserialized = deserialize( serialize( obj ), ClassWithoutDefaultConstructor.class ); assertDeepEquals( deserialized, obj ); } @Test( enabled = true ) public void testPrivateClass() throws Exception { final Holder<?> holder = new Holder<Object>( TestClasses.createPrivateClass( "foo" ) ); final Holder<?> deserialized = deserialize( serialize( holder ), Holder.class ); assertDeepEquals( deserialized, holder ); } @Test( enabled = true ) public void testCollections() throws Exception { final EntityWithCollections obj = new EntityWithCollections(); final EntityWithCollections deserialized = deserialize( serialize( obj ), EntityWithCollections.class ); assertDeepEquals( deserialized, obj ); } @Test( enabled = true ) public void testCyclicDependencies() throws Exception { 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 ); } public static class EntityWithCollections { private final String[] _bars; private final List<String> _foos; private final Map<String, Integer> _bazens; public EntityWithCollections() { _bars = new String[] { "foo", "bar" }; _foos = new ArrayList<String>( Arrays.asList( "foo", "bar" ) ); _bazens = new HashMap<String, Integer>(); _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<Object, Object>() ); } private static void assertDeepEquals( final Object one, final Object another, final Map<Object, Object> 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<? extends Map.Entry<?, ?>> iter1 = m1.entrySet().iterator(); final Iterator<? extends Map.Entry<?, ?>> iter2 = m2.entrySet().iterator(); while( iter1.hasNext() ) { Assert.assertTrue( iter2.hasNext() ); assertDeepEquals( iter1.next(), iter2.next(), 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() ); } Class<? extends Object> clazz = one.getClass(); while ( clazz != null ) { assertEqualDeclaredFields( clazz, one, another, alreadyChecked ); clazz = clazz.getSuperclass(); } } private static void assertEqualDeclaredFields( final Class<? extends Object> clazz, final Object one, final Object another, final Map<Object, Object> 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> T deserialize( final byte[] in, final Class<T> clazz ) { return deserialize(_kryo, in, clazz); } public static <T> T deserialize(final Kryo kryo, final byte[] in, final Class<T> clazz) { final Input input = new Input(in); return kryo.readObject(input, clazz); } }