/*
* Copyright 2014-2016 CyberVision, Inc.
*
* 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 org.kaaproject.kaa.server.common.core.algorithms.delta;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericArray;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericEnumSymbol;
import org.apache.avro.generic.GenericFixed;
import org.apache.avro.generic.GenericRecord;
import org.apache.commons.compress.utils.IOUtils;
import org.junit.Test;
import org.kaaproject.kaa.server.common.core.configuration.BaseData;
import org.kaaproject.kaa.server.common.core.schema.BaseSchema;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
public class DefaultDeltaCalculatorTest {
public static final String NEW_COMPLEX_CONFIG = "delta/complexFieldsDeltaNew.json";
public static final String OLD_COMPLEX_CONFIG = "delta/complexFieldsDeltaCurrent.json";
public static final String COMPLEX_SCHEMA = "delta/complexFieldsDeltaSchema.json";
public static final String COMPLEX_PROTOCOL_SCHEMA = "delta/complexFieldsDeltaProtocolSchema.json";
private static Schema getArraySchema(GenericRecord delta, String field) {
List<Schema> fieldTypes = delta.getSchema().getField(field).schema().getTypes();
for (Schema type : fieldTypes) {
if (type.getType() == Schema.Type.ARRAY) {
return type;
}
}
return null;
}
private static Schema getDeltaSchemaByFullName(Schema deltaSchema, String fullName) {
Schema deltaT = deltaSchema.getElementType();
Schema deltaUnion = deltaT.getField("delta").schema();
List<Schema> deltas = deltaUnion.getTypes();
for (Schema delta : deltas) {
if (delta.getFullName().equals(fullName)) {
return delta;
}
}
return null;
}
private static Schema getSchemaByFullName(List<Schema> types, String fullName) {
for (Schema type : types) {
if (type.getFullName().equals(fullName)) {
return type;
}
}
return null;
}
public static AvroBinaryDelta getComplexFieldDelta(Schema schema) {
GenericRecord delta = new GenericData.Record(getDeltaSchemaByFullName(schema, "org.kaa.config.testT"));
GenericEnumSymbol unchanged = new GenericData.EnumSymbol(getSchemaByFullName(delta.getSchema().getField("testField1").schema().getTypes(),
"org.kaaproject.configuration.unchangedT"), "unchanged");
GenericRecord testField2 = new GenericData.Record(getSchemaByFullName(delta.getSchema().getField("testField2").schema().getTypes(),
"org.kaa.config.testRecordT"));
testField2.put("testField3", 456);
byte[] rawUuid = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
GenericFixed uuid = new GenericData.Fixed(delta.getSchema().getField("__uuid").schema(), rawUuid);
delta.put("testField1", unchanged);
delta.put("testField2", testField2);
delta.put("__uuid", uuid);
AvroBinaryDelta deltaExpected = new AvroBinaryDelta(schema);
deltaExpected.addDelta(delta);
return deltaExpected;
}
@Test
public void testPrimitiveFieldsDelta() throws IOException, DeltaCalculatorException {
URL protocolSchemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/primitiveFieldsDeltaProtocolSchema.json");
Schema protocolSchema = new Schema.Parser().parse(new File(protocolSchemaUrl.getPath()));
URL schemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/primitiveFieldsDeltaSchema.json");
Schema schema = new Schema.Parser().parse(new File(schemaUrl.getPath()));
DeltaCalculationAlgorithm calculator = new DefaultDeltaCalculationAlgorithm(protocolSchema, schema);
ByteArrayOutputStream baosOld = new ByteArrayOutputStream();
URL oldConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/primitiveFieldsDeltaCurrent.json");
IOUtils.copy(new FileInputStream(oldConfigUrl.getPath()), baosOld, 1024);
String oldStr = new String(baosOld.toByteArray(), "UTF-8");
ByteArrayOutputStream baosNew = new ByteArrayOutputStream();
URL newConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/primitiveFieldsDeltaNew.json");
IOUtils.copy(new FileInputStream(newConfigUrl.getPath()), baosNew, 1024);
String newStr = new String(baosNew.toByteArray(), "UTF-8");
BaseData oldData = new BaseData(new BaseSchema(schema.toString()), oldStr);
BaseData newData = new BaseData(new BaseSchema(schema.toString()), newStr);
RawBinaryDelta deltaResult = calculator.calculate(oldData, newData);
GenericRecord delta = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testT"));
GenericEnumSymbol unchanged = new GenericData.EnumSymbol(getSchemaByFullName(delta.getSchema().getField("testField1").schema().getTypes(),
"org.kaaproject.configuration.unchangedT"), "unchanged");
byte[] rawUuid = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
GenericFixed uuid = new GenericData.Fixed(delta.getSchema().getField("__uuid").schema(), rawUuid);
delta.put("testField1", unchanged);
delta.put("testField2", 456);
delta.put("__uuid", uuid);
AvroBinaryDelta deltaExpected = new AvroBinaryDelta(protocolSchema);
deltaExpected.addDelta(delta);
assertArrayEquals(deltaExpected.getData(), deltaResult.getData());
}
@Test
public void testComplexFieldsDelta() throws IOException, URISyntaxException, DeltaCalculatorException {
URL protocolSchemaUrl = Thread.currentThread().getContextClassLoader().getResource(COMPLEX_PROTOCOL_SCHEMA);
URL schemaUrl = Thread.currentThread().getContextClassLoader().getResource(COMPLEX_SCHEMA);
Schema protocolSchema = new Schema.Parser().parse(new File(protocolSchemaUrl.getPath()));
Schema schema = new Schema.Parser().parse(new File(schemaUrl.getPath()));
DeltaCalculationAlgorithm calculator = new DefaultDeltaCalculationAlgorithm(protocolSchema, schema);
ByteArrayOutputStream baosOld = new ByteArrayOutputStream();
URL oldConfigUrl = Thread.currentThread().getContextClassLoader().getResource(OLD_COMPLEX_CONFIG);
IOUtils.copy(new FileInputStream(oldConfigUrl.getPath()), baosOld, 1024);
String oldStr = new String(baosOld.toByteArray(), "UTF-8");
ByteArrayOutputStream baosNew = new ByteArrayOutputStream();
URL newConfigUrl = Thread.currentThread().getContextClassLoader().getResource(NEW_COMPLEX_CONFIG);
IOUtils.copy(new FileInputStream(newConfigUrl.getPath()), baosNew, 1024);
String newStr = new String(baosNew.toByteArray(), "UTF-8");
BaseData oldData = new BaseData(new BaseSchema(schema.toString()), oldStr);
BaseData newData = new BaseData(new BaseSchema(schema.toString()), newStr);
RawBinaryDelta deltaResult = calculator.calculate(oldData, newData);
AvroBinaryDelta deltaExpected = getComplexFieldDelta(protocolSchema);
assertTrue(deltaResult.hasChanges());
assertArrayEquals(deltaExpected.getData(), deltaResult.getData());
assertTrue(deltaResult.hasChanges());
}
@Test
public void testArrayFieldsDelta() throws IOException, URISyntaxException, DeltaCalculatorException {
URL protocolSchemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayFieldsDeltaProtocolSchema.json");
Schema protocolSchema = new Schema.Parser().parse(new File(protocolSchemaUrl.getPath()));
URL schemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayFieldsDeltaSchema.json");
Schema schema = new Schema.Parser().parse(new File(schemaUrl.getPath()));
DeltaCalculationAlgorithm calculator = new DefaultDeltaCalculationAlgorithm(protocolSchema, schema);
ByteArrayOutputStream baosOld = new ByteArrayOutputStream();
URL oldConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayFieldsDeltaCurrent.json");
IOUtils.copy(new FileInputStream(oldConfigUrl.getPath()), baosOld, 1024);
String oldStr = new String(baosOld.toByteArray(), "UTF-8");
ByteArrayOutputStream baosNew = new ByteArrayOutputStream();
URL newConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayFieldsDeltaNew.json");
IOUtils.copy(new FileInputStream(newConfigUrl.getPath()), baosNew, 1024);
String newStr = new String(baosNew.toByteArray(), "UTF-8");
BaseData oldData = new BaseData(new BaseSchema(schema.toString()), oldStr);
BaseData newData = new BaseData(new BaseSchema(schema.toString()), newStr);
RawBinaryDelta deltaResult = calculator.calculate(oldData, newData);
/* The first delta - the item object was changed */
GenericRecord delta1 = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testRecordItemT"));
byte[] rawUuid1 = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3};
GenericFixed uuid1 = new GenericData.Fixed(delta1.getSchema().getField("__uuid").schema(), rawUuid1);
delta1.put("testField4", 36);
delta1.put("__uuid", uuid1);
/* The second delta - one item was removed from the array */
GenericRecord delta2 = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testT"));
byte[] rawUuid2 = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
GenericFixed uuid2 = new GenericData.Fixed(delta2.getSchema().getField("__uuid").schema(), rawUuid2);
GenericEnumSymbol unchanged = new GenericData.EnumSymbol(getSchemaByFullName(delta2.getSchema().getField("testField1").schema().getTypes(),
"org.kaaproject.configuration.unchangedT"), "unchanged");
delta2.put("testField1", unchanged);
delta2.put("__uuid", uuid2);
delta2.put("testField5", unchanged);
GenericRecord testField21 = new GenericData.Record(getSchemaByFullName(delta2.getSchema().getField("testField2").schema().getTypes(),
"org.kaa.config.testRecordT"));
GenericArray<GenericFixed> testField31 = new GenericData.Array<GenericFixed>(1, getArraySchema(testField21, "testField3"));
byte[] rawUuidToDelete = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
GenericFixed uuidToDelete = new GenericData.Fixed(getSchemaByFullName(testField31.getSchema().getElementType().getTypes(),
"org.kaaproject.configuration.uuidT"), rawUuidToDelete);
testField31.add(uuidToDelete);
testField21.put("testField3", testField31);
delta2.put("testField2", testField21);
/* The third delta - one new item was added to the array */
GenericRecord delta3 = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testT"));
delta3.put("testField1", unchanged);
delta3.put("__uuid", uuid2);
GenericRecord testField22 = new GenericData.Record(getSchemaByFullName(delta3.getSchema().getField("testField2").schema().getTypes(),
"org.kaa.config.testRecordT"));
GenericArray<GenericRecord> testField32 = new GenericData.Array<GenericRecord>(1, getArraySchema(testField22, "testField3"));
GenericRecord itemRecord = new GenericData.Record(getSchemaByFullName(testField32.getSchema().getElementType().getTypes(),
"org.kaa.config.testRecordItemT"));
itemRecord.put("testField4", 4);
byte[] rawNewRecordUuid = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4};
GenericFixed newRecordUuid = new GenericData.Fixed(itemRecord.getSchema().getField("__uuid").schema(), rawNewRecordUuid);
itemRecord.put("__uuid", newRecordUuid);
testField32.add(itemRecord);
testField22.put("testField3", testField32);
delta3.put("testField2", testField22);
AvroBinaryDelta deltaExpected = new AvroBinaryDelta(protocolSchema);
deltaExpected.addDelta(delta1);
deltaExpected.addDelta(delta2);
deltaExpected.addDelta(delta3);
assertArrayEquals(deltaExpected.getData(), deltaResult.getData());
}
@Test
public void testArrayPrimitiveFieldsDelta() throws IOException, URISyntaxException, DeltaCalculatorException {
URL protocolSchemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayPrimitiveFieldsDeltaProtocolSchema.json");
Schema protocolSchema = new Schema.Parser().parse(new File(protocolSchemaUrl.getPath()));
URL schemaUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayPrimitiveFieldsDeltaSchema.json");
Schema schema = new Schema.Parser().parse(new File(schemaUrl.getPath()));
DeltaCalculationAlgorithm calculator = new DefaultDeltaCalculationAlgorithm(protocolSchema, schema);
ByteArrayOutputStream baosOld = new ByteArrayOutputStream();
URL oldConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayPrimitiveFieldsDeltaCurrent.json");
IOUtils.copy(new FileInputStream(oldConfigUrl.getPath()), baosOld, 1024);
String oldStr = new String(baosOld.toByteArray(), "UTF-8");
ByteArrayOutputStream baosNew = new ByteArrayOutputStream();
URL newConfigUrl = Thread.currentThread().getContextClassLoader().getResource("delta/arrayPrimitiveFieldsDeltaNew.json");
IOUtils.copy(new FileInputStream(newConfigUrl.getPath()), baosNew, 1024);
String newStr = new String(baosNew.toByteArray(), "UTF-8");
BaseData oldData = new BaseData(new BaseSchema(schema.toString()), oldStr);
BaseData newData = new BaseData(new BaseSchema(schema.toString()), newStr);
RawBinaryDelta deltaResult = calculator.calculate(oldData, newData);
GenericRecord delta1 = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testT"));
GenericEnumSymbol reset = new GenericData.EnumSymbol(getSchemaByFullName(delta1.getSchema().getField("testField1").schema().getTypes(),
"org.kaaproject.configuration.resetT"), "reset");
byte[] rawUuid = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
GenericFixed uuid = new GenericData.Fixed(delta1.getSchema().getField("__uuid").schema(), rawUuid);
delta1.put("testField1", reset);
delta1.put("__uuid", uuid);
GenericRecord delta2 = new GenericData.Record(getDeltaSchemaByFullName(protocolSchema, "org.kaa.config.testT"));
delta2.put("__uuid", uuid);
GenericArray<Integer> testField1 = new GenericData.Array<Integer>(3, getArraySchema(delta2, "testField1"));
testField1.add(321);
testField1.add(456);
testField1.add(654);
delta2.put("testField1", testField1);
AvroBinaryDelta deltaExpected = new AvroBinaryDelta(protocolSchema);
deltaExpected.addDelta(delta1);
deltaExpected.addDelta(delta2);
assertArrayEquals(deltaExpected.getData(), deltaResult.getData());
}
@Test
public void testIdenticalConfigurations() throws IOException, URISyntaxException, DeltaCalculatorException {
URL protocolSchemaUrl = Thread.currentThread().getContextClassLoader().getResource(COMPLEX_PROTOCOL_SCHEMA);
URL schemaUrl = Thread.currentThread().getContextClassLoader().getResource(COMPLEX_SCHEMA);
Schema protocolSchema = new Schema.Parser().parse(new File(protocolSchemaUrl.getPath()));
Schema schema = new Schema.Parser().parse(new File(schemaUrl.getPath()));
DeltaCalculationAlgorithm calculator = new DefaultDeltaCalculationAlgorithm(protocolSchema, schema);
ByteArrayOutputStream baosOld = new ByteArrayOutputStream();
URL oldConfigUrl = Thread.currentThread().getContextClassLoader().getResource(OLD_COMPLEX_CONFIG);
IOUtils.copy(new FileInputStream(oldConfigUrl.getPath()), baosOld, 1024);
String oldStr = new String(baosOld.toByteArray(), "UTF-8");
ByteArrayOutputStream baosNew = new ByteArrayOutputStream();
URL newConfigUrl = Thread.currentThread().getContextClassLoader().getResource(OLD_COMPLEX_CONFIG);
IOUtils.copy(new FileInputStream(newConfigUrl.getPath()), baosNew, 1024);
String newStr = new String(baosNew.toByteArray(), "UTF-8");
BaseData oldData = new BaseData(new BaseSchema(schema.toString()), oldStr);
BaseData newData = new BaseData(new BaseSchema(schema.toString()), newStr);
RawBinaryDelta deltaResult = calculator.calculate(oldData, newData);
assertFalse(deltaResult.hasChanges());
assertNull(deltaResult.getData());
assertFalse(deltaResult.hasChanges());
}
}