From 498e494a1b809469552d1a82906f381504cd3a30 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 30 Jul 2017 21:20:18 +0200 Subject: [PATCH] Updated kryo unit tests from upstream --- .../kryo/CompatibleFieldSerializerTest.java | 491 ++++++- .../network/kryo/FieldSerializerTest.java | 1213 +++++++++++------ .../kryo/pool/KryoPoolBenchmarkTest.java | 188 +++ 3 files changed, 1417 insertions(+), 475 deletions(-) create mode 100644 test/dorkbox/network/kryo/pool/KryoPoolBenchmarkTest.java diff --git a/test/dorkbox/network/kryo/CompatibleFieldSerializerTest.java b/test/dorkbox/network/kryo/CompatibleFieldSerializerTest.java index c82578f8..bffcda4c 100644 --- a/test/dorkbox/network/kryo/CompatibleFieldSerializerTest.java +++ b/test/dorkbox/network/kryo/CompatibleFieldSerializerTest.java @@ -15,62 +15,197 @@ * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package dorkbox.network.kryo; +import java.io.FileNotFoundException; + import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer; +import com.esotericsoftware.kryo.serializers.FieldSerializer; -/** @author Nathan Sweet */ -@SuppressWarnings({"rawtypes"}) -public class CompatibleFieldSerializerTest extends KryoTestCase { +/** + * @author Nathan Sweet + */ +public +class CompatibleFieldSerializerTest extends KryoTestCase { { - this.supportsCopy = true; + supportsCopy = true; } - public void testCompatibleFieldSerializer () { + public + void testCompatibleFieldSerializer() throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); object1.other = new AnotherClass(); object1.other.value = "meow"; - this.kryo.setDefaultSerializer(CompatibleFieldSerializer.class); - this.kryo.register(TestClass.class); - this.kryo.register(AnotherClass.class); - roundTrip(100, 100, object1); + kryo.setDefaultSerializer(CompatibleFieldSerializer.class); + kryo.register(TestClass.class); + kryo.register(AnotherClass.class); + roundTrip(107, 107, object1); } - public void testAddedField () { + public + void testAddedField() throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); object1.other = new AnotherClass(); object1.other.value = "meow"; - CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(this.kryo, TestClass.class); + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); serializer.removeField("text"); - this.kryo.register(TestClass.class, serializer); - this.kryo.register(AnotherClass.class, new CompatibleFieldSerializer(this.kryo, AnotherClass.class)); - roundTrip(74, 74, object1); + kryo.register(TestClass.class, serializer); + kryo.register(AnotherClass.class, new CompatibleFieldSerializer(kryo, AnotherClass.class)); + roundTrip(80, 80, object1); - this.kryo.register(TestClass.class, new CompatibleFieldSerializer(this.kryo, TestClass.class)); - Object object2 = this.kryo.readClassAndObject(this.input); + kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); + Object object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); } - public void testRemovedField () { + public + void testAddedFieldToClassWithManyFields() throws FileNotFoundException { + // class must have more than CompatibleFieldSerializer#THRESHOLD_BINARY_SEARCH number of fields + ClassWithManyFields object1 = new ClassWithManyFields(); + object1.aa = "aa"; + object1.a0 = "a0"; + object1.bb = "bb"; + object1.b0 = "b0"; + object1.cc = "cc"; + object1.c0 = "c0"; + object1.dd = "dd"; + object1.d0 = "d0"; + object1.ee = "ee"; + object1.e0 = "e0"; + object1.ff = "ff"; + object1.f0 = "f0"; + object1.gg = "gg"; + object1.g0 = "g0"; + object1.hh = "hh"; + object1.h0 = "h0"; + object1.ii = "ii"; + object1.i0 = "i0"; + object1.jj = "jj"; + object1.j0 = "j0"; + object1.kk = "kk"; + object1.k0 = "k0"; + object1.ll = "ll"; + object1.mm = "mm"; + object1.nn = "nn"; + object1.oo = "oo"; + object1.pp = "pp"; + object1.qq = "qq"; + object1.rr = "rr"; + object1.ss = "ss"; + object1.tt = "tt"; + object1.uu = "uu"; + object1.vv = "vv"; + object1.ww = "ww"; + object1.xx = "xx"; + object1.yy = "yy"; + object1.zz = "zzaa"; + + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithManyFields.class); + serializer.removeField("bAdd"); + kryo.register(ClassWithManyFields.class, serializer); + roundTrip(226, 226, object1); + + kryo.register(ClassWithManyFields.class, new CompatibleFieldSerializer(kryo, ClassWithManyFields.class)); + Object object2 = kryo.readClassAndObject(input); + assertEquals(object1, object2); + } + + public + void testRemovedField() throws FileNotFoundException { TestClass object1 = new TestClass(); object1.child = new TestClass(); - this.kryo.register(TestClass.class, new CompatibleFieldSerializer(this.kryo, TestClass.class)); - roundTrip(88, 88, object1); + kryo.register(TestClass.class, new CompatibleFieldSerializer(kryo, TestClass.class)); + roundTrip(94, 94, object1); - CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(this.kryo, TestClass.class); + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, TestClass.class); serializer.removeField("text"); - this.kryo.register(TestClass.class, serializer); - Object object2 = this.kryo.readClassAndObject(this.input); + kryo.register(TestClass.class, serializer); + Object object2 = kryo.readClassAndObject(input); assertEquals(object1, object2); } - static public class TestClass { + public + void testRemovedFieldFromClassWithManyFields() throws FileNotFoundException { + // class must have more than CompatibleFieldSerializer#THRESHOLD_BINARY_SEARCH number of fields + ClassWithManyFields object1 = new ClassWithManyFields(); + object1.aa = "aa"; + object1.a0 = "a0"; + object1.bAdd = "bAdd"; + object1.bb = "bb"; + object1.b0 = "b0"; + object1.cc = "cc"; + object1.c0 = "c0"; + object1.dd = "dd"; + object1.d0 = "d0"; + object1.ee = "ee"; + object1.e0 = "e0"; + object1.ff = "ff"; + object1.f0 = "f0"; + object1.gg = "gg"; + object1.g0 = "g0"; + object1.hh = "hh"; + object1.h0 = "h0"; + object1.ii = "ii"; + object1.i0 = "i0"; + object1.jj = "jj"; + object1.j0 = "j0"; + object1.kk = "kk"; + object1.k0 = "k0"; + object1.ll = "ll"; + object1.mm = "mm"; + object1.nn = "nn"; + object1.oo = "oo"; + object1.pp = "pp"; + object1.qq = "qq"; + object1.rr = "rr"; + object1.ss = "ss"; + object1.tt = "tt"; + object1.uu = "uu"; + object1.vv = "vv"; + object1.ww = "ww"; + object1.xx = "xx"; + object1.yy = "yy"; + object1.zz = "zzaa"; + + + kryo.register(ClassWithManyFields.class, new CompatibleFieldSerializer(kryo, ClassWithManyFields.class)); + roundTrip(236, 236, object1); + + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ClassWithManyFields.class); + serializer.removeField("bAdd"); + kryo.register(ClassWithManyFields.class, serializer); + Object object2 = kryo.readClassAndObject(input); + assertTrue(object2 instanceof ClassWithManyFields); + assertNull("the bAdd field should be null", ((ClassWithManyFields) object2).bAdd); + // update the field in order to verify the remainder of the object was deserialized correctly + ((ClassWithManyFields) object2).bAdd = object1.bAdd; + assertEquals(object1, object2); + } + + public + void testExtendedClass() throws FileNotFoundException { + ExtendedTestClass extendedObject = new ExtendedTestClass(); + + // this test would fail with DEFAULT field name strategy + kryo.getFieldSerializerConfig() + .setCachedFieldNameStrategy(FieldSerializer.CachedFieldNameStrategy.EXTENDED); + + CompatibleFieldSerializer serializer = new CompatibleFieldSerializer(kryo, ExtendedTestClass.class); + kryo.register(ExtendedTestClass.class, serializer); + roundTrip(286, 286, extendedObject); + + ExtendedTestClass object2 = (ExtendedTestClass) kryo.readClassAndObject(input); + assertEquals(extendedObject, object2); + } + + static public + class TestClass { public String text = "something"; public int moo = 120; public long moo2 = 1234120; @@ -78,51 +213,299 @@ public class CompatibleFieldSerializerTest extends KryoTestCase { public int zzz = 123; public AnotherClass other; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - TestClass other = (TestClass)obj; - if (this.child == null) { - if (other.child != null) { + TestClass other = (TestClass) obj; + if (child == null) { + if (other.child != null) return false; - } - } else if (!this.child.equals(other.child)) { - return false; } - if (this.moo != other.moo) { + else if (!child.equals(other.child)) return false; - } - if (this.moo2 != other.moo2) { + if (moo != other.moo) return false; - } - if (this.text == null) { - if (other.text != null) { + if (moo2 != other.moo2) + return false; + if (text == null) { + if (other.text != null) return false; - } - } else if (!this.text.equals(other.text)) { - return false; } - if (this.zzz != other.zzz) { + else if (!text.equals(other.text)) + return false; + if (zzz != other.zzz) return false; - } return true; } + } - @Override - public int hashCode() { - return super.hashCode(); + + static public + class ExtendedTestClass extends TestClass { + // keep the same names of attributes like TestClass + public String text = "extendedSomething"; + public int moo = 127; + public long moo2 = 5555; + public TestClass child; + public int zzz = 222; + public AnotherClass other; + + public + boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ExtendedTestClass other = (ExtendedTestClass) obj; + + if (!super.equals(obj)) + return false; + if (child == null) { + if (other.child != null) + return false; + } + else if (!child.equals(other.child)) + return false; + if (moo != other.moo) + return false; + if (moo2 != other.moo2) + return false; + if (text == null) { + if (other.text != null) + return false; + } + else if (!text.equals(other.text)) + return false; + if (zzz != other.zzz) + return false; + return true; } } - static public class AnotherClass { + + static public + class AnotherClass { String value; } + + + static public + class ClassWithManyFields { + public String aa; + public String bb; + public String bAdd; + public String cc; + public String dd; + public String ee; + public String ff; + public String gg; + public String hh; + public String ii; + public String jj; + public String kk; + public String ll; + public String mm; + public String nn; + public String oo; + public String pp; + public String qq; + public String rr; + public String ss; + public String tt; + public String uu; + public String vv; + public String ww; + public String xx; + public String yy; + public String zz; + public String a0; + public String b0; + public String c0; + public String d0; + public String e0; + public String f0; + public String g0; + public String h0; + public String i0; + public String j0; + public String k0; + + + @Override + public + boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final ClassWithManyFields that = (ClassWithManyFields) o; + + if (aa != null ? !aa.equals(that.aa) : that.aa != null) { + return false; + } + if (bb != null ? !bb.equals(that.bb) : that.bb != null) { + return false; + } + if (bAdd != null ? !bAdd.equals(that.bAdd) : that.bAdd != null) { + return false; + } + if (cc != null ? !cc.equals(that.cc) : that.cc != null) { + return false; + } + if (dd != null ? !dd.equals(that.dd) : that.dd != null) { + return false; + } + if (ee != null ? !ee.equals(that.ee) : that.ee != null) { + return false; + } + if (ff != null ? !ff.equals(that.ff) : that.ff != null) { + return false; + } + if (gg != null ? !gg.equals(that.gg) : that.gg != null) { + return false; + } + if (hh != null ? !hh.equals(that.hh) : that.hh != null) { + return false; + } + if (ii != null ? !ii.equals(that.ii) : that.ii != null) { + return false; + } + if (jj != null ? !jj.equals(that.jj) : that.jj != null) { + return false; + } + if (kk != null ? !kk.equals(that.kk) : that.kk != null) { + return false; + } + if (ll != null ? !ll.equals(that.ll) : that.ll != null) { + return false; + } + if (mm != null ? !mm.equals(that.mm) : that.mm != null) { + return false; + } + if (nn != null ? !nn.equals(that.nn) : that.nn != null) { + return false; + } + if (oo != null ? !oo.equals(that.oo) : that.oo != null) { + return false; + } + if (pp != null ? !pp.equals(that.pp) : that.pp != null) { + return false; + } + if (qq != null ? !qq.equals(that.qq) : that.qq != null) { + return false; + } + if (rr != null ? !rr.equals(that.rr) : that.rr != null) { + return false; + } + if (ss != null ? !ss.equals(that.ss) : that.ss != null) { + return false; + } + if (tt != null ? !tt.equals(that.tt) : that.tt != null) { + return false; + } + if (uu != null ? !uu.equals(that.uu) : that.uu != null) { + return false; + } + if (vv != null ? !vv.equals(that.vv) : that.vv != null) { + return false; + } + if (ww != null ? !ww.equals(that.ww) : that.ww != null) { + return false; + } + if (xx != null ? !xx.equals(that.xx) : that.xx != null) { + return false; + } + if (yy != null ? !yy.equals(that.yy) : that.yy != null) { + return false; + } + if (zz != null ? !zz.equals(that.zz) : that.zz != null) { + return false; + } + if (a0 != null ? !a0.equals(that.a0) : that.a0 != null) { + return false; + } + if (b0 != null ? !b0.equals(that.b0) : that.b0 != null) { + return false; + } + if (c0 != null ? !c0.equals(that.c0) : that.c0 != null) { + return false; + } + if (d0 != null ? !d0.equals(that.d0) : that.d0 != null) { + return false; + } + if (e0 != null ? !e0.equals(that.e0) : that.e0 != null) { + return false; + } + if (f0 != null ? !f0.equals(that.f0) : that.f0 != null) { + return false; + } + if (g0 != null ? !g0.equals(that.g0) : that.g0 != null) { + return false; + } + if (h0 != null ? !h0.equals(that.h0) : that.h0 != null) { + return false; + } + if (i0 != null ? !i0.equals(that.i0) : that.i0 != null) { + return false; + } + if (j0 != null ? !j0.equals(that.j0) : that.j0 != null) { + return false; + } + return k0 != null ? k0.equals(that.k0) : that.k0 == null; + } + + @Override + public + int hashCode() { + int result = aa != null ? aa.hashCode() : 0; + result = 31 * result + (bb != null ? bb.hashCode() : 0); + result = 31 * result + (bAdd != null ? bAdd.hashCode() : 0); + result = 31 * result + (cc != null ? cc.hashCode() : 0); + result = 31 * result + (dd != null ? dd.hashCode() : 0); + result = 31 * result + (ee != null ? ee.hashCode() : 0); + result = 31 * result + (ff != null ? ff.hashCode() : 0); + result = 31 * result + (gg != null ? gg.hashCode() : 0); + result = 31 * result + (hh != null ? hh.hashCode() : 0); + result = 31 * result + (ii != null ? ii.hashCode() : 0); + result = 31 * result + (jj != null ? jj.hashCode() : 0); + result = 31 * result + (kk != null ? kk.hashCode() : 0); + result = 31 * result + (ll != null ? ll.hashCode() : 0); + result = 31 * result + (mm != null ? mm.hashCode() : 0); + result = 31 * result + (nn != null ? nn.hashCode() : 0); + result = 31 * result + (oo != null ? oo.hashCode() : 0); + result = 31 * result + (pp != null ? pp.hashCode() : 0); + result = 31 * result + (qq != null ? qq.hashCode() : 0); + result = 31 * result + (rr != null ? rr.hashCode() : 0); + result = 31 * result + (ss != null ? ss.hashCode() : 0); + result = 31 * result + (tt != null ? tt.hashCode() : 0); + result = 31 * result + (uu != null ? uu.hashCode() : 0); + result = 31 * result + (vv != null ? vv.hashCode() : 0); + result = 31 * result + (ww != null ? ww.hashCode() : 0); + result = 31 * result + (xx != null ? xx.hashCode() : 0); + result = 31 * result + (yy != null ? yy.hashCode() : 0); + result = 31 * result + (zz != null ? zz.hashCode() : 0); + result = 31 * result + (a0 != null ? a0.hashCode() : 0); + result = 31 * result + (b0 != null ? b0.hashCode() : 0); + result = 31 * result + (c0 != null ? c0.hashCode() : 0); + result = 31 * result + (d0 != null ? d0.hashCode() : 0); + result = 31 * result + (e0 != null ? e0.hashCode() : 0); + result = 31 * result + (f0 != null ? f0.hashCode() : 0); + result = 31 * result + (g0 != null ? g0.hashCode() : 0); + result = 31 * result + (h0 != null ? h0.hashCode() : 0); + result = 31 * result + (i0 != null ? i0.hashCode() : 0); + result = 31 * result + (j0 != null ? j0.hashCode() : 0); + result = 31 * result + (k0 != null ? k0.hashCode() : 0); + return result; + } + } } diff --git a/test/dorkbox/network/kryo/FieldSerializerTest.java b/test/dorkbox/network/kryo/FieldSerializerTest.java index 93db4146..127e540a 100644 --- a/test/dorkbox/network/kryo/FieldSerializerTest.java +++ b/test/dorkbox/network/kryo/FieldSerializerTest.java @@ -19,6 +19,17 @@ */ package dorkbox.network.kryo; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.objenesis.strategy.StdInstantiatorStrategy; + import com.esotericsoftware.kryo.DefaultSerializer; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; @@ -27,14 +38,15 @@ import com.esotericsoftware.kryo.Registration; import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.CollectionSerializer; +import com.esotericsoftware.kryo.serializers.CollectionSerializer.BindCollection; +import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.IntArraySerializer; +import com.esotericsoftware.kryo.serializers.DefaultArraySerializers.LongArraySerializer; +import com.esotericsoftware.kryo.serializers.DefaultSerializers.StringSerializer; import com.esotericsoftware.kryo.serializers.FieldSerializer; +import com.esotericsoftware.kryo.serializers.FieldSerializer.Bind; import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional; -import org.objenesis.strategy.StdInstantiatorStrategy; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import com.esotericsoftware.kryo.serializers.MapSerializer.BindMap; /** @author Nathan Sweet */ @@ -108,6 +120,28 @@ public class FieldSerializerTest extends KryoTestCase { this.supportsCopy = true; } + public + void testFieldRemovalOnGenerics() { + kryo.register(IsGeneric.class); + kryo.register(DefaultTypes.class); + kryo.register(byte[].class); + + FieldSerializer serializer = new FieldSerializer(kryo, IsGeneric.class); + serializer.removeField("y"); + kryo.register(IsGeneric.class, serializer); + + IsGeneric> test = new IsGeneric>(); + test.item = new IsGeneric(); + + try { + roundTrip(5, 11, test); + } catch (KryoException e) { + e.printStackTrace(); + fail("Couldn't serialize generic with a removed field."); + } + } + + public void testOptionalRegistration () { this.kryo.setRegistrationRequired(false); DefaultTypes test = new DefaultTypes(); @@ -355,13 +389,83 @@ public class FieldSerializerTest extends KryoTestCase { roundTrip(4, 4, test); } - public void testGenericTypes () { - this.kryo = new Kryo(); - this.kryo.setRegistrationRequired(true); - this.kryo.register(HasGenerics.class); - this.kryo.register(ArrayList.class); - this.kryo.register(ArrayList[].class); - this.kryo.register(HashMap.class); + /** + * This test uses StdInstantiatorStrategy and therefore requires a no-arg constructor. + **/ + @SuppressWarnings("synthetic-access") + public + void testDefaultInstantiatorStrategy() { + kryo.register(HasArgumentConstructor.class); + HasArgumentConstructor test = new HasPrivateConstructor(); + HasPrivateConstructor.invocations = 0; + + kryo.register(HasPrivateConstructor.class); + roundTrip(4, 4, test); + assertEquals("Default constructor should not be invoked with StdInstantiatorStrategy strategy", 25, + HasPrivateConstructor.invocations); + } + + /** + * This test uses StdInstantiatorStrategy and should bypass invocation of no-arg constructor, even if it is provided. + **/ + @SuppressWarnings("synthetic-access") + public + void testStdInstantiatorStrategy() { + kryo.register(HasArgumentConstructor.class); + kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); + HasArgumentConstructor test = new HasPrivateConstructor(); + HasPrivateConstructor.invocations = 0; + + kryo.register(HasPrivateConstructor.class); + roundTrip(4, 4, test); + assertEquals("Default constructor should not be invoked with StdInstantiatorStrategy strategy", 0, + HasPrivateConstructor.invocations); + } + + public + void testGenericTypesOptimized() { + testGenericTypes(true); + } + + public + void testGenericTypesNonOptimized() { + testGenericTypes(false); + } + + /** + * Check that it is OK to change the optimizedGenerics setting on the same Kryo instance multiple times. + */ + public + void testGenericTypesOptimizedAndNonOptimized() { + Kryo kryoInstance = kryo; + testGenericTypes(true); + assertEquals("The same instance of Kryo should be used", kryoInstance, kryo); + testGenericTypes(false); + assertEquals("The same instance of Kryo should be used", kryoInstance, kryo); + testGenericTypes(true); + assertEquals("The same instance of Kryo should be used", kryoInstance, kryo); + testGenericTypes(false); + assertEquals("The same instance of Kryo should be used", kryoInstance, kryo); + } + + private + void testGenericTypes(boolean optimizedGenerics) { + kryo.getFieldSerializerConfig() + .setOptimizedGenerics(optimizedGenerics); + kryo.setReferences(true); + kryo.setRegistrationRequired(true); + kryo.register(HasGenerics.class); + kryo.register(ArrayList.class); + kryo.register(ArrayList[].class); + kryo.register(HashMap.class); + + // It may happen that classes were registered already befor this function + // was called. In this case, invoke the setters on the FieldSerializer + // objects directly. + FieldSerializer fieldSerializer; + fieldSerializer = (FieldSerializer) kryo.getSerializer(HasGenerics.class); + fieldSerializer.setOptimizedGenerics(optimizedGenerics); + HasGenerics test = new HasGenerics(); test.list1 = new ArrayList(); test.list1.add(1); @@ -383,9 +487,9 @@ public class FieldSerializerTest extends KryoTestCase { test.list5 = new ArrayList(); test.list5.add("one"); test.list5.add("two"); - roundTrip(53, 80, test); + roundTrip(optimizedGenerics ? 53 : 56, optimizedGenerics ? 80 : 83, test); ArrayList[] al = new ArrayList[1]; - al[0] = new ArrayList(Arrays.asList(new String[] { "A", "B", "S" })); + al[0] = new ArrayList(Arrays.asList(new String[] {"A", "B", "S"})); roundTrip(18, 18, al); } @@ -406,7 +510,205 @@ public class FieldSerializerTest extends KryoTestCase { roundTrip(75, 95, test); } - static public class DefaultTypes { + public + void testTransients() { + kryo.register(HasTransients.class); + HasTransients objectWithTransients1 = new HasTransients(); + objectWithTransients1.transientField1 = "Test"; + objectWithTransients1.anotherField2 = 5; + objectWithTransients1.anotherField3 = "Field2"; + + FieldSerializer ser = (FieldSerializer) kryo.getSerializer(HasTransients.class); + ser.setCopyTransient(false); + + HasTransients objectWithTransients3 = kryo.copy(objectWithTransients1); + assertTrue("Objects should be different if copy does not include transient fields", + !objectWithTransients3.equals(objectWithTransients1)); + assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); + + ser.setCopyTransient(true); + HasTransients objectWithTransients2 = kryo.copy(objectWithTransients1); + assertEquals("Objects should be equal if copy includes transient fields", objectWithTransients2, objectWithTransients1); + } + + public + void testTransientsUsingGlobalConfig() { + kryo.getFieldSerializerConfig() + .setCopyTransient(false); + kryo.register(HasTransients.class); + HasTransients objectWithTransients1 = new HasTransients(); + objectWithTransients1.transientField1 = "Test"; + objectWithTransients1.anotherField2 = 5; + objectWithTransients1.anotherField3 = "Field2"; + + FieldSerializer ser = (FieldSerializer) kryo.getSerializer(HasTransients.class); + HasTransients objectWithTransients3 = kryo.copy(objectWithTransients1); + assertTrue("Objects should be different if copy does not include transient fields", + !objectWithTransients3.equals(objectWithTransients1)); + assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); + + ser.setCopyTransient(true); + HasTransients objectWithTransients2 = kryo.copy(objectWithTransients1); + assertEquals("Objects should be equal if copy includes transient fields", objectWithTransients2, objectWithTransients1); + } + + public + void testSerializeTransients() { + kryo.register(HasTransients.class); + HasTransients objectWithTransients1 = new HasTransients(); + objectWithTransients1.transientField1 = "Test"; + objectWithTransients1.anotherField2 = 5; + objectWithTransients1.anotherField3 = "Field2"; + + ByteArrayOutputStream outputStream; + Output output; + Input input; + byte[] outBytes; + + FieldSerializer ser = (FieldSerializer) kryo.getSerializer(HasTransients.class); + ser.setSerializeTransient(false); + + outputStream = new ByteArrayOutputStream(); + output = new Output(outputStream); + ser.write(kryo, output, objectWithTransients1); + output.flush(); + + outBytes = outputStream.toByteArray(); + input = new Input(outBytes); + HasTransients objectWithTransients3 = ser.read(kryo, input, HasTransients.class); + assertTrue("Objects should be different if write does not include transient fields", + !objectWithTransients3.equals(objectWithTransients1)); + assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); + + ser.setSerializeTransient(true); + + outputStream = new ByteArrayOutputStream(); + output = new Output(outputStream); + ser.write(kryo, output, objectWithTransients1); + output.flush(); + + outBytes = outputStream.toByteArray(); + input = new Input(outBytes); + HasTransients objectWithTransients2 = ser.read(kryo, input, HasTransients.class); + assertTrue("Objects should be equal if write includes transient fields", objectWithTransients2.equals(objectWithTransients1)); + } + + public + void testSerializeTransientsUsingGlobalConfig() { + kryo.getFieldSerializerConfig() + .setSerializeTransient(false); + kryo.register(HasTransients.class); + HasTransients objectWithTransients1 = new HasTransients(); + objectWithTransients1.transientField1 = "Test"; + objectWithTransients1.anotherField2 = 5; + objectWithTransients1.anotherField3 = "Field2"; + + ByteArrayOutputStream outputStream; + Output output; + Input input; + byte[] outBytes; + + FieldSerializer ser = (FieldSerializer) kryo.getSerializer(HasTransients.class); + outputStream = new ByteArrayOutputStream(); + output = new Output(outputStream); + ser.write(kryo, output, objectWithTransients1); + output.flush(); + + outBytes = outputStream.toByteArray(); + input = new Input(outBytes); + HasTransients objectWithTransients3 = ser.read(kryo, input, HasTransients.class); + assertTrue("Objects should be different if write does not include transient fields", + !objectWithTransients3.equals(objectWithTransients1)); + assertEquals("transient fields should be null", objectWithTransients3.transientField1, null); + + ser.setSerializeTransient(true); + + outputStream = new ByteArrayOutputStream(); + output = new Output(outputStream); + ser.write(kryo, output, objectWithTransients1); + output.flush(); + + outBytes = outputStream.toByteArray(); + input = new Input(outBytes); + HasTransients objectWithTransients2 = ser.read(kryo, input, HasTransients.class); + assertTrue("Objects should be equal if write includes transient fields", objectWithTransients2.equals(objectWithTransients1)); + } + + public + void testCorrectlyAnnotatedFields() { + kryo.register(int[].class); + kryo.register(long[].class); + kryo.register(HashMap.class); + kryo.register(ArrayList.class); + kryo.register(AnnotatedFields.class); + AnnotatedFields obj1 = new AnnotatedFields(); + obj1.map = new HashMap(); + obj1.map.put("key1", new int[] {1, 2, 3}); + obj1.map.put("key2", new int[] {3, 4, 5}); + obj1.map.put("key3", null); + + obj1.collection = new ArrayList(); + obj1.collection.add(new long[] {1, 2, 3}); + + roundTrip(31, 73, obj1); + } + + public + void testWronglyAnnotatedCollectionFields() { + try { + kryo.register(WronglyAnnotatedCollectionFields.class); + WronglyAnnotatedCollectionFields obj1 = new WronglyAnnotatedCollectionFields(); + roundTrip(31, 73, obj1); + } catch (RuntimeException e) { + Throwable cause = e.getCause() + .getCause(); + assertTrue("Exception should complain about a field not implementing java.util.Collection", + cause.getMessage() + .contains("be used only with fields implementing java.util.Collection")); + return; + } + + assertFalse("Exception was expected", true); + } + + public + void testWronglyAnnotatedMapFields() { + try { + kryo.register(WronglyAnnotatedMapFields.class); + WronglyAnnotatedMapFields obj1 = new WronglyAnnotatedMapFields(); + roundTrip(31, 73, obj1); + } catch (RuntimeException e) { + Throwable cause = e.getCause() + .getCause(); + assertTrue("Exception should complain about a field not implementing java.util.Map ", + cause.getMessage() + .contains("be used only with fields implementing java.util.Map")); + return; + } + + assertFalse("Exception was expected", true); + } + + public + void testMultipleTimesAnnotatedMapFields() { + try { + kryo.register(MultipleTimesAnnotatedCollectionFields.class); + MultipleTimesAnnotatedCollectionFields obj1 = new MultipleTimesAnnotatedCollectionFields(); + roundTrip(31, 73, obj1); + } catch (RuntimeException e) { + Throwable cause = e.getCause() + .getCause(); + assertTrue("Exception should complain about a field that has a serializer already", + cause.getMessage() + .contains("already")); + return; + } + + assertFalse("Exception was expected", true); + } + + static public + class DefaultTypes { // Primitives. public boolean booleanField; public byte byteField; @@ -432,608 +734,560 @@ public class FieldSerializerTest extends KryoTestCase { DefaultTypes child; HasStringField hasStringField; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - DefaultTypes other = (DefaultTypes)obj; - if (this.BooleanField == null) { - if (other.BooleanField != null) { + DefaultTypes other = (DefaultTypes) obj; + if (BooleanField == null) { + if (other.BooleanField != null) return false; - } - } else if (!this.BooleanField.equals(other.BooleanField)) { - return false; } - if (this.ByteField == null) { - if (other.ByteField != null) { + else if (!BooleanField.equals(other.BooleanField)) + return false; + if (ByteField == null) { + if (other.ByteField != null) return false; - } - } else if (!this.ByteField.equals(other.ByteField)) { - return false; } - if (this.CharacterField == null) { - if (other.CharacterField != null) { + else if (!ByteField.equals(other.ByteField)) + return false; + if (CharacterField == null) { + if (other.CharacterField != null) return false; - } - } else if (!this.CharacterField.equals(other.CharacterField)) { - return false; } - if (this.DoubleField == null) { - if (other.DoubleField != null) { + else if (!CharacterField.equals(other.CharacterField)) + return false; + if (DoubleField == null) { + if (other.DoubleField != null) return false; - } - } else if (!this.DoubleField.equals(other.DoubleField)) { - return false; } - if (this.FloatField == null) { - if (other.FloatField != null) { + else if (!DoubleField.equals(other.DoubleField)) + return false; + if (FloatField == null) { + if (other.FloatField != null) return false; - } - } else if (!this.FloatField.equals(other.FloatField)) { - return false; } - if (this.IntegerField == null) { - if (other.IntegerField != null) { + else if (!FloatField.equals(other.FloatField)) + return false; + if (IntegerField == null) { + if (other.IntegerField != null) return false; - } - } else if (!this.IntegerField.equals(other.IntegerField)) { - return false; } - if (this.LongField == null) { - if (other.LongField != null) { + else if (!IntegerField.equals(other.IntegerField)) + return false; + if (LongField == null) { + if (other.LongField != null) return false; - } - } else if (!this.LongField.equals(other.LongField)) { - return false; } - if (this.ShortField == null) { - if (other.ShortField != null) { + else if (!LongField.equals(other.LongField)) + return false; + if (ShortField == null) { + if (other.ShortField != null) return false; - } - } else if (!this.ShortField.equals(other.ShortField)) { - return false; } - if (this.StringField == null) { - if (other.StringField != null) { + else if (!ShortField.equals(other.ShortField)) + return false; + if (StringField == null) { + if (other.StringField != null) return false; - } - } else if (!this.StringField.equals(other.StringField)) { - return false; } - if (this.booleanField != other.booleanField) { + else if (!StringField.equals(other.StringField)) + return false; + if (booleanField != other.booleanField) return false; - } - Object list1 = arrayToList(this.byteArrayField); + Object list1 = arrayToList(byteArrayField); Object list2 = arrayToList(other.byteArrayField); if (list1 != list2) { - if (list1 == null || list2 == null) { + if (list1 == null || list2 == null) return false; - } - if (!list1.equals(list2)) { + if (!list1.equals(list2)) return false; - } } - if (this.child != other.child) { - if (this.child == null || other.child == null) { + if (child != other.child) { + if (child == null || other.child == null) return false; - } - if (this.child != this && !this.child.equals(other.child)) { + if (child != this && !child.equals(other.child)) return false; - } } - if (this.byteField != other.byteField) { + if (byteField != other.byteField) return false; - } - if (this.charField != other.charField) { + if (charField != other.charField) return false; - } - if (Double.doubleToLongBits(this.doubleField) != Double.doubleToLongBits(other.doubleField)) { + if (Double.doubleToLongBits(doubleField) != Double.doubleToLongBits(other.doubleField)) return false; - } - if (Float.floatToIntBits(this.floatField) != Float.floatToIntBits(other.floatField)) { + if (Float.floatToIntBits(floatField) != Float.floatToIntBits(other.floatField)) return false; - } - if (this.intField != other.intField) { + if (intField != other.intField) return false; - } - if (this.longField != other.longField) { + if (longField != other.longField) return false; - } - if (this.shortField != other.shortField) { + if (shortField != other.shortField) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public final class A { + + static public final + class A { public int value; public B b; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - A other = (A)obj; - if (this.b == null) { - if (other.b != null) { + A other = (A) obj; + if (b == null) { + if (other.b != null) return false; - } - } else if (!this.b.equals(other.b)) { - return false; } - if (this.value != other.value) { + else if (!b.equals(other.b)) + return false; + if (value != other.value) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public final class B { + + static public final + class B { public int value; public A a; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - B other = (B)obj; - if (this.a == null) { - if (other.a != null) { + B other = (B) obj; + if (a == null) { + if (other.a != null) return false; - } - } else if (!this.a.equals(other.a)) { - return false; } - if (this.value != other.value) { + else if (!a.equals(other.a)) + return false; + if (value != other.value) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static final class C { + + static public final + class C { public A a; public D d; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - C other = (C)obj; - if (this.a == null) { - if (other.a != null) { + C other = (C) obj; + if (a == null) { + if (other.a != null) return false; - } - } else if (!this.a.equals(other.a)) { - return false; } - if (this.d == null) { - if (other.d != null) { + else if (!a.equals(other.a)) + return false; + if (d == null) { + if (other.d != null) return false; - } - } else if (!this.d.equals(other.d)) { - return false; } + else if (!d.equals(other.d)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public final class D { + + static public final + class D { public E e; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - D other = (D)obj; - if (this.e == null) { - if (other.e != null) { + D other = (D) obj; + if (e == null) { + if (other.e != null) return false; - } - } else if (!this.e.equals(other.e)) { - return false; } + else if (!e.equals(other.e)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public final class E { + + static public final + class E { public F f; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - E other = (E)obj; - if (this.f == null) { - if (other.f != null) { + E other = (E) obj; + if (f == null) { + if (other.f != null) return false; - } - } else if (!this.f.equals(other.f)) { - return false; } + else if (!f.equals(other.f)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public final class F { + + static public final + class F { public int value; public final int finalValue = 12; public A a; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - F other = (F)obj; - if (this.finalValue != other.finalValue) { + F other = (F) obj; + if (finalValue != other.finalValue) return false; - } - if (this.value != other.value) { + if (value != other.value) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public class SimpleNoDefaultConstructor { + + static public + class SimpleNoDefaultConstructor { int constructorValue; - public SimpleNoDefaultConstructor (int constructorValue) { + public + SimpleNoDefaultConstructor(int constructorValue) { this.constructorValue = constructorValue; } - public int getConstructorValue () { - return this.constructorValue; + public + int getConstructorValue() { + return constructorValue; } - @Override - public int hashCode () { + public + int hashCode() { final int prime = 31; int result = 1; - result = prime * result + this.constructorValue; + result = prime * result + constructorValue; return result; } - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - SimpleNoDefaultConstructor other = (SimpleNoDefaultConstructor)obj; - if (this.constructorValue != other.constructorValue) { + SimpleNoDefaultConstructor other = (SimpleNoDefaultConstructor) obj; + if (constructorValue != other.constructorValue) return false; - } return true; } } - static public class ComplexNoDefaultConstructor { + + static public + class HasTransients { + public transient String transientField1; + public int anotherField2; + public String anotherField3; + + public + HasTransients() { + } + + public + int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + anotherField2; + result = prime * result + ((anotherField3 == null) ? 0 : anotherField3.hashCode()); + result = prime * result + ((transientField1 == null) ? 0 : transientField1.hashCode()); + return result; + } + + public + boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HasTransients other = (HasTransients) obj; + if (anotherField2 != other.anotherField2) + return false; + if (anotherField3 == null) { + if (other.anotherField3 != null) + return false; + } + else if (!anotherField3.equals(other.anotherField3)) + return false; + if (transientField1 == null) { + if (other.transientField1 != null) + return false; + } + else if (!transientField1.equals(other.transientField1)) + return false; + return true; + } + } + + + static public + class ComplexNoDefaultConstructor { public transient String name; public int anotherField1; public String anotherField2; - public ComplexNoDefaultConstructor (String name) { + public + ComplexNoDefaultConstructor(String name) { this.name = name; } - @Override - public int hashCode () { + public + int hashCode() { final int prime = 31; int result = 1; - result = prime * result + this.anotherField1; - result = prime * result + (this.anotherField2 == null ? 0 : this.anotherField2.hashCode()); - result = prime * result + (this.name == null ? 0 : this.name.hashCode()); + result = prime * result + anotherField1; + result = prime * result + ((anotherField2 == null) ? 0 : anotherField2.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - ComplexNoDefaultConstructor other = (ComplexNoDefaultConstructor)obj; - if (this.anotherField1 != other.anotherField1) { + ComplexNoDefaultConstructor other = (ComplexNoDefaultConstructor) obj; + if (anotherField1 != other.anotherField1) return false; - } - if (this.anotherField2 == null) { - if (other.anotherField2 != null) { + if (anotherField2 == null) { + if (other.anotherField2 != null) return false; - } - } else if (!this.anotherField2.equals(other.anotherField2)) { - return false; } - if (this.name == null) { - if (other.name != null) { + else if (!anotherField2.equals(other.anotherField2)) + return false; + if (name == null) { + if (other.name != null) return false; - } - } else if (!this.name.equals(other.name)) { - return false; } + else if (!name.equals(other.name)) + return false; return true; } } - static public class HasNonNull { - @NotNull public String nonNullText; - @Override - public boolean equals (Object obj) { - if (this == obj) { + static public + class HasNonNull { + @NotNull + public String nonNullText; + + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasNonNull other = (HasNonNull)obj; - if (this.nonNullText == null) { - if (other.nonNullText != null) { + HasNonNull other = (HasNonNull) obj; + if (nonNullText == null) { + if (other.nonNullText != null) return false; - } - } else if (!this.nonNullText.equals(other.nonNullText)) { - return false; } + else if (!nonNullText.equals(other.nonNullText)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public class HasStringField { + + static public + class HasStringField { public String text; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasStringField other = (HasStringField)obj; - if (this.text == null) { - if (other.text != null) { + HasStringField other = (HasStringField) obj; + if (text == null) { + if (other.text != null) return false; - } - } else if (!this.text.equals(other.text)) { - return false; } + else if (!text.equals(other.text)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public class HasOptionalAnnotation { - @Optional("smurf") int moo; - @Override - public boolean equals (Object obj) { - if (this == obj) { + static public + class HasOptionalAnnotation { + @Optional("smurf") + int moo; + + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasOptionalAnnotation other = (HasOptionalAnnotation)obj; - if (this.moo != other.moo) { + HasOptionalAnnotation other = (HasOptionalAnnotation) obj; + if (moo != other.moo) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } + @DefaultSerializer(HasDefaultSerializerAnnotationSerializer.class) - static public class HasDefaultSerializerAnnotation { + static public + class HasDefaultSerializerAnnotation { long time; - public HasDefaultSerializerAnnotation (long time) { + public + HasDefaultSerializerAnnotation(long time) { this.time = time; } - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasDefaultSerializerAnnotation other = (HasDefaultSerializerAnnotation)obj; - if (this.time != other.time) { + HasDefaultSerializerAnnotation other = (HasDefaultSerializerAnnotation) obj; + if (time != other.time) return false; - } return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public class HasDefaultSerializerAnnotationSerializer extends Serializer { + + static public + class HasDefaultSerializerAnnotationSerializer extends Serializer { @Override - public void write (Kryo kryo, Output output, HasDefaultSerializerAnnotation object) { + public + void write(Kryo kryo, Output output, HasDefaultSerializerAnnotation object) { output.writeLong(object.time, true); } @Override - public HasDefaultSerializerAnnotation read (Kryo kryo, Input input, Class type) { + public + HasDefaultSerializerAnnotation read(Kryo kryo, Input input, Class type) { return new HasDefaultSerializerAnnotation(input.readLong(true)); } @Override - public HasDefaultSerializerAnnotation copy (Kryo kryo, HasDefaultSerializerAnnotation original) { + public + HasDefaultSerializerAnnotation copy(Kryo kryo, HasDefaultSerializerAnnotation original) { return new HasDefaultSerializerAnnotation(original.time); } } - static public class HasArgumentConstructor { + + static public + class HasArgumentConstructor { public String moo; - public HasArgumentConstructor (String moo) { + public + HasArgumentConstructor(String moo) { this.moo = moo; } - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasArgumentConstructor other = (HasArgumentConstructor)obj; - if (this.moo == null) { - if (other.moo != null) { + HasArgumentConstructor other = (HasArgumentConstructor) obj; + if (moo == null) { + if (other.moo != null) return false; - } - } else if (!this.moo.equals(other.moo)) { - return false; } + else if (!moo.equals(other.moo)) + return false; return true; } - - @Override - public int hashCode() { - return super.hashCode(); - } } - static public class HasPrivateConstructor extends HasArgumentConstructor { - private HasPrivateConstructor () { + + static public + class HasPrivateConstructor extends HasArgumentConstructor { + static int invocations; + + private + HasPrivateConstructor() { super("cow"); + HasPrivateConstructor.invocations++; } } - static public class HasGenerics { + + static public + class HasGenerics { ArrayList list1; List> list2 = new ArrayList>(); List list3 = new ArrayList(); @@ -1041,66 +1295,183 @@ public class FieldSerializerTest extends KryoTestCase { ArrayList list5; HashMap> map1; - @Override - public boolean equals (Object obj) { - if (this == obj) { + public + boolean equals(Object obj) { + if (this == obj) return true; - } - if (obj == null) { + if (obj == null) return false; - } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) return false; - } - HasGenerics other = (HasGenerics)obj; - if (this.list1 == null) { - if (other.list1 != null) { + HasGenerics other = (HasGenerics) obj; + if (list1 == null) { + if (other.list1 != null) return false; - } - } else if (!this.list1.equals(other.list1)) { - return false; } - if (this.list2 == null) { - if (other.list2 != null) { - return false; - } - } else if (!this.list2.equals(other.list2)) { + else if (!list1.equals(other.list1)) return false; + if (list2 == null) { + if (other.list2 != null) + return false; } - if (this.list3 == null) { - if (other.list3 != null) { - return false; - } - } else if (!this.list3.equals(other.list3)) { + else if (!list2.equals(other.list2)) return false; + if (list3 == null) { + if (other.list3 != null) + return false; } - if (this.list4 == null) { - if (other.list4 != null) { - return false; - } - } else if (!this.list4.equals(other.list4)) { + else if (!list3.equals(other.list3)) return false; + if (list4 == null) { + if (other.list4 != null) + return false; } - if (this.list5 == null) { - if (other.list5 != null) { - return false; - } - } else if (!this.list5.equals(other.list5)) { + else if (!list4.equals(other.list4)) return false; + if (list5 == null) { + if (other.list5 != null) + return false; } - if (this.map1 == null) { - if (other.map1 != null) { - return false; - } - } else if (!this.map1.equals(other.map1)) { + else if (!list5.equals(other.list5)) return false; + if (map1 == null) { + if (other.map1 != null) + return false; + } + else if (!map1.equals(other.map1)) + return false; + return true; + } + } + + + static public + class MultipleTimesAnnotatedCollectionFields { + // This annotation should result in an exception, because + // it is applied to a non-collection field + @BindCollection(elementSerializer = LongArraySerializer.class, + elementClass = long[].class, + elementsCanBeNull = false) + @Bind(CollectionSerializer.class) + Collection collection; + } + + + static public + class WronglyAnnotatedCollectionFields { + // This annotation should result in an exception, because + // it is applied to a non-collection field + @BindCollection(elementSerializer = LongArraySerializer.class, + elementClass = long[].class, + elementsCanBeNull = false) + int collection; + } + + + static public + class WronglyAnnotatedMapFields { + // This annotation should result in an exception, because + // it is applied to a non-map field + @BindMap(valueSerializer = IntArraySerializer.class, + keySerializer = StringSerializer.class, + valueClass = int[].class, + keyClass = String.class, + keysCanBeNull = false) + Object map; + } + + + static public + class AnnotatedFields { + @Bind(StringSerializer.class) + Object stringField; + + @BindMap(valueSerializer = IntArraySerializer.class, + keySerializer = StringSerializer.class, + valueClass = int[].class, + keyClass = String.class, + keysCanBeNull = false) + Map map; + + @BindCollection(elementSerializer = LongArraySerializer.class, + elementClass = long[].class, + elementsCanBeNull = false) + Collection collection; + + public + boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AnnotatedFields other = (AnnotatedFields) obj; + if (map == null) { + if (other.map != null) + return false; + } + else { + if (other.map == null) + return false; + if (map.size() != other.map.size()) + return false; + for (Object e : map.entrySet()) { + Map.Entry entry = (Map.Entry) e; + if (!other.map.containsKey(entry.getKey())) + return false; + Object otherValue = other.map.get(entry.getKey()); + if (entry.getValue() == null && otherValue != null) + return false; + if (!Arrays.equals((int[]) entry.getValue(), (int[]) otherValue)) + return false; + } + } + if (collection == null) { + if (other.collection != null) + return false; + } + else { + if (other.collection == null) + return false; + if (collection.size() != other.collection.size()) + return false; + Iterator it1 = collection.iterator(); + Iterator it2 = other.collection.iterator(); + while (it1.hasNext()) { + Object e1 = it1.next(); + Object e2 = it2.next(); + if (!Arrays.equals((long[]) e1, (long[]) e2)) + return false; + } } return true; } + } + + + static public + class IsGeneric { + T item; + private int y; + private int z; @Override - public int hashCode() { - return super.hashCode(); + public + boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof IsGeneric)) + return false; + + IsGeneric isGeneric = (IsGeneric) o; + + if (z != isGeneric.z) + return false; + if (item != null ? !item.equals(isGeneric.item) : isGeneric.item != null) + return false; + + return true; } } } diff --git a/test/dorkbox/network/kryo/pool/KryoPoolBenchmarkTest.java b/test/dorkbox/network/kryo/pool/KryoPoolBenchmarkTest.java new file mode 100644 index 00000000..a68ab0b1 --- /dev/null +++ b/test/dorkbox/network/kryo/pool/KryoPoolBenchmarkTest.java @@ -0,0 +1,188 @@ +/* Copyright (c) 2008, Nathan Sweet + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package dorkbox.network.kryo.pool; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import com.esotericsoftware.kryo.Kryo; + +import dorkbox.network.kryo.FieldSerializerTest; +import dorkbox.objectPool.ObjectPool; +import dorkbox.objectPool.PoolableObject; + +public +class KryoPoolBenchmarkTest { + + private static final int WARMUP_ITERATIONS = 10000; + + /** + * Number of runs. + */ + private static final int RUN_CNT = 10; + + /** + * Number of iterations. Set it to something rather big for obtaining meaningful results + */ +// private static final int ITER_CNT = 200000; + private static final int ITER_CNT = 10000; + private static final int SLEEP_BETWEEN_RUNS = 100; + + // not private to prevent the synthetic accessor method + static PoolableObject poolableObject = new PoolableObject() { + @Override + public + Kryo create() { + Kryo kryo = new Kryo(); + kryo.register(FieldSerializerTest.DefaultTypes.class); + kryo.register(SampleObject.class); + return kryo; + } + }; + + @Test + public + void testWithoutPool() throws Exception { + // Warm-up phase: Perform 100000 iterations + runWithoutPool(1, WARMUP_ITERATIONS, false); + runWithoutPool(RUN_CNT, ITER_CNT, true); + } + + @Test + public + void testWithPool() throws Exception { + // Warm-up phase: Perform 100000 iterations + runWithPool(ObjectPool.NonBlocking(poolableObject), 1, WARMUP_ITERATIONS, false); + runWithPool(ObjectPool.NonBlocking(poolableObject), RUN_CNT, ITER_CNT, true); + } + + @Test + public + void testWithPoolWithSoftReferences() throws Exception { + // Warm-up phase: Perform 100000 iterations + runWithPool(ObjectPool.NonBlockingSoftReference(poolableObject), 1, WARMUP_ITERATIONS, false); + runWithPool(ObjectPool.NonBlockingSoftReference(poolableObject), RUN_CNT, ITER_CNT, true); + } + + private + void run(String description, Runnable runnable, final int runCount, final int iterCount, boolean outputResults) throws Exception { + long avgDur = 0; + long bestTime = Long.MAX_VALUE; + + for (int i = 0; i < runCount; i++) { + long start = System.nanoTime(); + + for (int j = 0; j < iterCount; j++) { + runnable.run(); + } + + long dur = System.nanoTime() - start; + dur = TimeUnit.NANOSECONDS.toMillis(dur); + + if (outputResults) + System.out.format(">>> %s (run %d): %,d ms\n", description, i + 1, dur); + avgDur += dur; + bestTime = Math.min(bestTime, dur); + systemCleanupAfterRun(); + } + + avgDur /= runCount; + + if (outputResults) { + System.out.format("\n>>> %s (average): %,d ms", description, avgDur); + System.out.format("\n>>> %s (best time): %,d ms\n\n", description, bestTime); + } + + } + + private + void runWithoutPool(final int runCount, final int iterCount, boolean outputResults) throws Exception { + run("Without pool", new Runnable() { + @Override + public + void run() { + poolableObject.create(); + } + }, runCount, iterCount, outputResults); + } + + private + void runWithPool(final ObjectPool pool, final int runCount, final int iterCount, boolean outputResults) throws Exception { + run("With pool " + pool.toString(), new Runnable() { + @Override + public + void run() { + Kryo kryo = pool.take(); + pool.put(kryo); + } + }, runCount, iterCount, outputResults); + } + + private + void systemCleanupAfterRun() throws InterruptedException { + System.gc(); + Thread.sleep(SLEEP_BETWEEN_RUNS); + System.gc(); + } + + private static + class SampleObject { + private int intVal; + private float floatVal; + private Short shortVal; + private long[] longArr; + private double[] dblArr; + private String str; + + public + SampleObject() { + } + + SampleObject(int intVal, float floatVal, Short shortVal, long[] longArr, double[] dblArr, String str) { + this.intVal = intVal; + this.floatVal = floatVal; + this.shortVal = shortVal; + this.longArr = longArr; + this.dblArr = dblArr; + this.str = str; + } + + /** + * {@inheritDoc} + */ + @Override + public + boolean equals(Object other) { + if (this == other) + return true; + + if (other == null || getClass() != other.getClass()) + return false; + + SampleObject obj = (SampleObject) other; + + return intVal == obj.intVal && floatVal == obj.floatVal && shortVal.equals(obj.shortVal) && Arrays.equals(dblArr, obj.dblArr) && + Arrays.equals(longArr, obj.longArr) && (str == null ? obj.str == null : str.equals(obj.str)); + } + } + +}