/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.nio.serialization.compatibility;
import com.hazelcast.config.SerializationConfig;
import com.hazelcast.config.SerializerConfig;
import com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder;
import com.hazelcast.internal.serialization.impl.HeapData;
import com.hazelcast.nio.serialization.ClassDefinition;
import com.hazelcast.nio.serialization.ClassDefinitionBuilder;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.serialization.SerializationService;
import com.hazelcast.test.HazelcastParametersRunnerFactory;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import static junit.framework.TestCase.assertTrue;
@RunWith(Parameterized.class)
@Parameterized.UseParametersRunnerFactory(HazelcastParametersRunnerFactory.class)
public class BinaryCompatibilityTest {
private static final int NULL_OBJECT = -1;
private static Map<String, Data> dataMap = new HashMap<String, Data>();
//OPTIONS
private static Object[] objects = ReferenceObjects.allTestObjects;
private static boolean[] unsafeAllowedOpts = {true, false};
private static ByteOrder[] byteOrders = {ByteOrder.BIG_ENDIAN, ByteOrder.LITTLE_ENDIAN};
private static byte[] versions = {1};
@Parameterized.Parameter(0)
public boolean allowUnsafe;
@Parameterized.Parameter(1)
public Object object;
@Parameterized.Parameter(2)
public ByteOrder byteOrder;
@Parameterized.Parameter(3)
public byte version;
@BeforeClass
public static void init() throws IOException {
for (byte version : versions) {
InputStream input = BinaryCompatibilityTest.class.getResourceAsStream("/" + createFileName(version));
DataInputStream inputStream = new DataInputStream(input);
while (input.available() != 0) {
String objectKey = inputStream.readUTF();
int length = inputStream.readInt();
if (length != NULL_OBJECT) {
byte[] bytes = new byte[length];
inputStream.read(bytes);
dataMap.put(objectKey, new HeapData(bytes));
}
}
inputStream.close();
}
}
@Parameterized.Parameters(name = "allowUnsafe:{0}, {index}, isBigEndian:{2}, version:{3}")
public static Iterable<Object[]> parameters() {
LinkedList<Object[]> parameters = new LinkedList<Object[]>();
for (boolean allowUnsafe : unsafeAllowedOpts) {
for (Object object : objects) {
for (ByteOrder byteOrder : byteOrders) {
for (byte version : versions) {
parameters.add(new Object[]{allowUnsafe, object, byteOrder, version});
}
}
}
}
return parameters;
}
private String createObjectKey() {
return version + "-" + (object == null ? "NULL" : object.getClass().getSimpleName()) + "-" + byteOrder;
}
private static String createFileName(byte version) {
return version + ".serialization.compatibility.binary";
}
private SerializationService createSerializationService() {
SerializationConfig config = new SerializationConfig();
{
SerializerConfig serializerConfig = new SerializerConfig();
serializerConfig.setImplementation(new CustomByteArraySerializer()).setTypeClass(CustomByteArraySerializable.class);
config.addSerializerConfig(serializerConfig);
}
{
SerializerConfig serializerConfig = new SerializerConfig();
serializerConfig.setImplementation(new CustomStreamSerializer()).setTypeClass(CustomStreamSerializable.class);
config.addSerializerConfig(serializerConfig);
}
config.setAllowUnsafe(allowUnsafe);
config.setByteOrder(byteOrder);
ClassDefinition classDefinition =
new ClassDefinitionBuilder(ReferenceObjects.PORTABLE_FACTORY_ID, ReferenceObjects.INNER_PORTABLE_CLASS_ID)
.addIntField("i").addFloatField("f").build();
return new DefaultSerializationServiceBuilder()
.setConfig(config)
.setVersion(version)
.addPortableFactory(ReferenceObjects.PORTABLE_FACTORY_ID, new APortableFactory())
.addDataSerializableFactory(ReferenceObjects.IDENTIFIED_DATA_SERIALIZABLE_FACTORY_ID,
new ADataSerializableFactory())
.addClassDefinition(classDefinition)
.build();
}
@Test
public void readAndVerifyBinaries() throws IOException {
String key = createObjectKey();
SerializationService serializationService = createSerializationService();
Object readObject = serializationService.toObject(dataMap.get(key));
boolean equals = equals(object, readObject);
if (!equals) {
System.out.println(object.getClass().getSimpleName() + ": " + object + " != " + readObject);
}
assertTrue(equals);
}
@Test
public void basicSerializeDeserialize() throws IOException {
SerializationService serializationService = createSerializationService();
Data data = serializationService.toData(object);
Object readObject = serializationService.toObject(data);
assertTrue(equals(object, readObject));
}
public static boolean equals(Object a, Object b) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.getClass().isArray() && b.getClass().isArray()) {
int length = Array.getLength(a);
if (length > 0 && !a.getClass().getComponentType().equals(b.getClass().getComponentType())) {
return false;
}
if (Array.getLength(b) != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (!equals(Array.get(a, i), Array.get(b, i))) {
return false;
}
}
return true;
}
if (a instanceof List && b instanceof List) {
ListIterator e1 = ((List) a).listIterator();
ListIterator e2 = ((List) b).listIterator();
while (e1.hasNext() && e2.hasNext()) {
Object o1 = e1.next();
Object o2 = e2.next();
if (!equals(o1, o2)) {
return false;
}
}
return !(e1.hasNext() || e2.hasNext());
}
return a.equals(b);
}
}