blob: 88444267e5ac8589e79db0f09f86c353066d247f [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.pdx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.internal.HeapDataOutputStream;
import com.gemstone.gemfire.internal.Version;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.pdx.PdxInstanceFactory;
import com.gemstone.gemfire.pdx.internal.EnumInfo.PdxInstanceEnumInfo;
import com.gemstone.gemfire.pdx.internal.PdxInstanceEnum;
import com.gemstone.gemfire.pdx.internal.PdxInstanceFactoryImpl;
import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
/**
* @author dsmith
*
*/
@Category(IntegrationTest.class)
public class PdxInstanceJUnitTest {
private GemFireCacheImpl c;
private int allFieldCount;
@Before
public void setUp() {
// make it a loner
this.c = (GemFireCacheImpl) new CacheFactory()
.set("mcast-port", "0")
.set("distributed-system-id", "255")
.setPdxReadSerialized(true)
.create();
}
@After
public void tearDown() {
this.c.close();
}
@Test
public void testGetField() throws IOException, ClassNotFoundException {
PdxInstance instance = getPdx(new TestPdx() {
public void toData(PdxWriter out) {
out.writeBoolean("field1", false);
out.writeInt("field2", 53);
out.writeObject("field3", new TestPdx() {
public void toData(PdxWriter writer) {
writer.writeString("afield", "hello");
}
});
}
});
assertEquals(Arrays.asList(new String[]{"field1", "field2", "field3"}), instance.getFieldNames());
assertEquals(instance.getField("field2"), Integer.valueOf(53));
assertEquals(instance.getField("field1"), Boolean.FALSE);
PdxInstance fieldInstance = (PdxInstance) instance.getField("field3");
assertEquals(Arrays.asList(new String[]{"afield"}), fieldInstance.getFieldNames());
assertEquals("hello", fieldInstance.getField("afield"));
}
@Test
public void testHashCodeAndEqualsSameType() throws IOException, ClassNotFoundException {
PdxInstance instance = getAllFields(0);
assertEquals(instance, getAllFields(0));
assertEquals(instance.hashCode(), getAllFields(0).hashCode());
for(int i =1; i < allFieldCount + 1; i++) {
PdxInstance other = getAllFields(i);
assertFalse("One field " + i + " hashcode have been unequal but were equal" + instance.getField("field" + (i-1)) +", " + other.getField("field" + (i-1)) + ", " + instance + ", " + other, instance.equals(other));
//Technically, this could be true. If this asserts fails I guess we can just change the test.
assertFalse("One field " + i + " hashcode have been unequal but were equal" + instance +", " + other, instance.hashCode() == other.hashCode());
}
}
@Test
public void testEquals() throws IOException, ClassNotFoundException {
PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testEquals", false);
c.writeInt("intField", 37);
PdxInstance pi = c.create();
assertEquals(false, pi.equals(null));
assertEquals(false, pi.equals(new Date(37)));
c = PdxInstanceFactoryImpl.newCreator("testEquals", false);
c.writeInt("intField", 37);
c.writeInt("intField2", 38);
PdxInstance pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date());
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", null);
pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{1});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new byte[]{(byte)1});
pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{1});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{2});
pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{1});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{1});
pi2 = c.create();
assertEquals(pi.hashCode(), pi2.hashCode());
assertEquals(true, pi.equals(pi2));
assertEquals(true, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new int[]{1});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date());
pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date[]{new Date(1)});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date[]{new Date(2)});
pi2 = c.create();
pi.hashCode();
pi2.hashCode();
assertEquals(false, pi.equals(pi2));
assertEquals(false, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date[]{new Date(1)});
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", new Date[]{new Date(1)});
pi2 = c.create();
assertEquals(pi.hashCode(), pi2.hashCode());
assertEquals(true, pi.equals(pi2));
assertEquals(true, pi2.equals(pi));
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", MyEnum.ONE);
pi = c.create();
c = PdxInstanceFactoryImpl.newCreator("testEqualsOF", false);
c.writeObject("objField", MyEnum.ONE);
pi2 = c.create();
assertEquals(pi.hashCode(), pi2.hashCode());
assertEquals(true, pi.equals(pi2));
assertEquals(true, pi2.equals(pi));
}
public enum MyEnum {ONE, TWO};
public enum MyComplexEnum {ONE{}, TWO{}};
public void testPdxComplexEnum() {
PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testPdxEnum", false);
c.writeObject("enumField", MyComplexEnum.ONE);
PdxInstance pi = c.create();
Object f = pi.getField("enumField");
if (f instanceof PdxInstanceEnumInfo) {
PdxInstanceEnumInfo e = (PdxInstanceEnumInfo) f;
assertEquals("ONE", e.getName());
GemFireCacheImpl theCache = GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed.");
theCache.getPdxRegistry().flushCache();
assertEquals(MyComplexEnum.ONE, e.getObject());
} else {
fail("Expected enumField to be a PdxInstanceEnumInfo but it was a " + f.getClass());
}
}
public void testPdxSimpleEnum() {
PdxInstanceFactory c = PdxInstanceFactoryImpl.newCreator("testPdxEnum", false);
c.writeObject("enumField", MyEnum.ONE);
PdxInstance pi = c.create();
Object f = pi.getField("enumField");
if (f instanceof PdxInstanceEnumInfo) {
PdxInstanceEnumInfo e = (PdxInstanceEnumInfo) f;
assertEquals("ONE", e.getName());
GemFireCacheImpl theCache = GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed.");
theCache.getPdxRegistry().flushCache();
assertEquals(MyEnum.ONE, e.getObject());
} else {
fail("Expected enumField to be a PdxInstanceEnumInfo but it was a " + f.getClass());
}
}
@Test
public void testEqualsDifferentTypes() throws IOException, ClassNotFoundException {
PdxInstance instance1 = getPdx(new TestPdx() {
public void toData(PdxWriter writer) {
writer.writeBoolean("field1", true);
}
});
PdxInstance instance2 = getPdx(new TestPdx() {
public void toData(PdxWriter writer) {
writer.writeBoolean("field1", true);
}
});
//These are different classes, so they shouldn't match.
assertFalse(instance1.equals(instance2));
assertFalse(instance1.isIdentityField("field1"));
}
@Test
public void testHashCodeEqualsOutOfOrderFields() throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
PdxSerializable serializable1 = getSeparateClassLoadedPdx(true);
PdxInstance instance1 = getPdx(serializable1);
PdxSerializable serializable2 = getSeparateClassLoadedPdx(false);
PdxInstance instance2 = getPdx(serializable2);
assertEquals(instance1, instance2);
assertEquals(instance1.hashCode(), instance2.hashCode());
}
@Test
public void testIdentityFields() throws IOException, ClassNotFoundException {
PdxInstance instance1 = getPdx(getPdxSerializable(new TestPdx() {
public void toData(PdxWriter out) {
out.writeInt("field2", 53);
out.writeBoolean("field1", false);
out.markIdentityField("field2");
}
}));
PdxInstance instance2 = getPdx(getPdxSerializable(new TestPdx() {
public void toData(PdxWriter out) {
out.writeInt("field2", 53);
out.writeBoolean("field1", true);
out.markIdentityField("field2");
}
}));
assertEquals(instance1, instance2);
assertEquals(instance1.hashCode(), instance2.hashCode());
assertFalse(instance1.isIdentityField("field1"));
assertTrue(instance1.isIdentityField("field2"));
}
//This is hack to make sure the classnames are the same
//for every call to this method, even if the toDatas are different.
private PdxSerializable getPdxSerializable(final TestPdx toData) {
return new TestPdx() {
public void toData(PdxWriter writer) {
toData.toData(writer);
}
};
}
private PdxSerializable getSeparateClassLoadedPdx(boolean field1First)
throws ClassNotFoundException, NoSuchMethodException,
InstantiationException, IllegalAccessException, InvocationTargetException {
ClassLoader parent = Thread.currentThread().getContextClassLoader();
ClassLoader loader1 = new NonDelegatingLoader(parent);
Class clazz1 = loader1.loadClass(getClass().getPackage().getName() + ".SeparateClassloaderPdx");
Constructor constructor = clazz1.getConstructor(boolean.class);
constructor.setAccessible(true);
PdxSerializable serializable = (PdxSerializable) constructor.newInstance(Boolean.valueOf(field1First));
return serializable;
}
public PdxInstance getAllFields(final int change) throws IOException, ClassNotFoundException {
PdxInstance instance = getPdx(new TestPdx() {
public void toData(PdxWriter out) {
int x = 0;
Number serializable1 = new BigInteger("1234");
Number serializable2 = new BigInteger("1235");
Collection collection1 = new ArrayList();
collection1.add(serializable1);
collection1.add(serializable2);
Collection collection2 = new ArrayList();
collection2.add(serializable2);
collection2.add(serializable1);
SimpleClass testPdx1 = new SimpleClass(5, (byte) 5);
SimpleClass testPdx2 = new SimpleClass(6, (byte) 6);
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map2.put(serializable1, serializable2);
out.writeChar("field" + x++, change==x ? 'c': 'd');
out.writeBoolean("field" + x++, change==x ? true: false);
out.writeByte("field" + x++, (byte) (change==x ? 0x5: 0x6));
out.writeShort("field" + x++, (short) (change==x ? 7 : 8 ));
out.writeInt("field" + x++, change==x ? 9 : 10 );
out.writeLong("field" + x++, change==x ? 55555555: 666666666);
out.writeFloat("field" + x++, change==x ? 23.44f: 23.55f);
out.writeDouble("field" + x++, change==x ? 28.7777: 29.3333);
out.writeDate("field" + x++, change==x ? new Date(23L): new Date(25L));
// out.writeEnum("field" + x++, change==x ? Enum<?> e: );
out.writeString("field" + x++, change==x ? "hello": "world");
out.writeObject("field" + x++, change==x ? serializable1: serializable2 );
out.writeObject("field" + x++, change==x ? testPdx1: testPdx2 );
out.writeObject("field" + x++, change==x ? map1: map2);
out.writeObject("field" + x++, change==x ? collection1: collection2);
// out.writeRegion("field" + x++, change==x ? region1: region2);
out.writeBooleanArray("field" + x++, change==x ? new boolean[] {false, true}: new boolean[] {true, false});
out.writeCharArray("field" + x++, change==x ? new char[] {'a', 'b'}: new char[] {'a', 'c'} );
out.writeByteArray("field" + x++, change==x ? new byte[] {0, 1, 2}: new byte[] {0, 1, 3});
out.writeShortArray("field" + x++, change==x ? new short[] {0, 1, 4}: new short[] {0, 1, 5});
out.writeIntArray("field" + x++, change==x ? new int[] {0, 1, 4}: new int[] {0, 1, 5});
out.writeLongArray("field" + x++, change==x ? new long[] {0, 1, 4}: new long[] {0, 1, 5});
out.writeFloatArray("field" + x++, change==x ? new float[] {0, 1, 4}: new float[] {0, 1, 5});
out.writeDoubleArray("field" + x++, change==x ? new double[] {0, 1, 4}: new double[] {0, 1, 5});
out.writeStringArray("field" + x++, change==x ? new String[] {"a", "b"}: new String[] {"a", "c"});
out.writeObjectArray("field" + x++, change==x ? new Object[] {collection1, serializable1}: new Object[] {serializable2});
out.writeArrayOfByteArrays("field" + x++, change==x ? new byte[][] { {1, 2}}: new byte[][] { {2, 2}});
allFieldCount = x;
}
});
return instance;
}
private PdxInstance getPdx(PdxSerializable toData) throws IOException, ClassNotFoundException {
HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
DataSerializer.writeObject(toData, out);
return DataSerializer.readObject(new DataInputStream(new ByteArrayInputStream(out.toByteArray())));
}
static abstract class TestPdx implements PdxSerializable {
public void fromData(PdxReader in) {
}
}
}