/* * Copyright 2010 Outerthought bvba * * 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.lilyproject.avro; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import org.apache.avro.Schema; import org.apache.avro.generic.GenericData; import org.easymock.IMocksControl; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.lilyproject.repository.api.FieldType; import org.lilyproject.repository.api.FieldTypeEntry; import org.lilyproject.repository.api.FieldTypes; import org.lilyproject.repository.api.IdGenerator; import org.lilyproject.repository.api.LRepository; import org.lilyproject.repository.api.QName; import org.lilyproject.repository.api.Record; import org.lilyproject.repository.api.RecordFactory; import org.lilyproject.repository.api.RecordId; import org.lilyproject.repository.api.RecordType; import org.lilyproject.repository.api.SchemaId; import org.lilyproject.repository.api.Scope; import org.lilyproject.repository.api.TypeManager; import org.lilyproject.repository.api.ValueType; import org.lilyproject.repository.impl.FieldTypeEntryImpl; import org.lilyproject.repository.impl.FieldTypeImpl; import org.lilyproject.repository.impl.RecordImpl; import org.lilyproject.repository.impl.RecordTypeImpl; import org.lilyproject.repository.impl.id.IdGeneratorImpl; import org.lilyproject.repository.impl.id.SchemaIdImpl; import org.lilyproject.repository.impl.valuetype.StringValueType; import static org.easymock.EasyMock.createControl; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import static org.junit.Assert.assertEquals; public class AvroConverterTest { private static LRepository repository; private static TypeManager typeManager; private static RecordFactory recordFactory; private static AvroConverter converter; private static IMocksControl control; @BeforeClass public static void setUpBeforeClass() throws Exception { control = createControl(); repository = control.createMock(LRepository.class); typeManager = control.createMock(TypeManager.class); recordFactory = control.createMock(RecordFactory.class); } @AfterClass public static void tearDownAfterClass() throws Exception { } @Before public void setUp() throws Exception { repository.getTypeManager(); expectLastCall().andReturn(typeManager).anyTimes(); repository.getRecordFactory(); expectLastCall().andReturn(recordFactory).anyTimes(); } @After public void tearDown() throws Exception { control.reset(); } @Test public void testQName() { control.replay(); converter = new AvroConverter(); // Full name QName qname = new QName("namespace", "name"); AvroQName avroQName = new AvroQName(); avroQName.namespace = "namespace"; avroQName.name = "name"; assertEquals(avroQName, converter.convert(qname)); assertEquals(qname, converter.convert(avroQName)); } @Test public void testValueType() throws Exception { ValueType valueType = new StringValueType(); typeManager.getValueType("STRING"); expectLastCall().andReturn(valueType); control.replay(); converter = new AvroConverter(); AvroValueType avroValueType = new AvroValueType(); avroValueType.valueType = "STRING"; assertEquals(valueType, converter.convert(avroValueType, typeManager)); assertEquals(avroValueType, converter.convert(valueType)); control.verify(); } @Test public void testFieldTypeEntry() { SchemaId id = new SchemaIdImpl(UUID.randomUUID()); FieldTypeEntry fieldTypeEntry = new FieldTypeEntryImpl(id, true); typeManager.newFieldTypeEntry(id, true); expectLastCall().andReturn(fieldTypeEntry); control.replay(); converter = new AvroConverter(); AvroFieldTypeEntry avroFieldTypeEntry = new AvroFieldTypeEntry(); AvroSchemaId avroSchemaId = new AvroSchemaId(); avroSchemaId.idBytes = ByteBuffer.wrap(id.getBytes()); avroFieldTypeEntry.id = avroSchemaId; avroFieldTypeEntry.mandatory = true; assertEquals(fieldTypeEntry, converter.convert(avroFieldTypeEntry, typeManager)); assertEquals(avroFieldTypeEntry, converter.convert(fieldTypeEntry)); control.verify(); } @Test public void testFieldType() throws Exception { ValueType valueType = new StringValueType(); typeManager.getValueType("STRING"); expectLastCall().andReturn(valueType); QName name = new QName("aNamespace", "aName"); SchemaId fieldTypeId = new SchemaIdImpl(UUID.randomUUID()); FieldType fieldType = new FieldTypeImpl(fieldTypeId, valueType, name, Scope.NON_VERSIONED); typeManager.newFieldType(fieldTypeId, valueType, name, Scope.NON_VERSIONED); expectLastCall().andReturn(fieldType); control.replay(); converter = new AvroConverter(); AvroFieldType avroFieldType = new AvroFieldType(); AvroSchemaId avroSchemaId = new AvroSchemaId(); avroSchemaId.idBytes = ByteBuffer.wrap(fieldTypeId.getBytes()); avroFieldType.id = avroSchemaId; AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroFieldType.name = avroQName; avroFieldType.scope = AvroScope.NON_VERSIONED; AvroValueType avroValueType = new AvroValueType(); avroValueType.valueType = "STRING"; avroFieldType.valueType = avroValueType; assertEquals(fieldType, converter.convert(avroFieldType, typeManager)); assertEquals(avroFieldType, converter.convert(fieldType)); control.verify(); } @Test public void testFieldTypeWithoutId() throws Exception { ValueType valueType = new StringValueType(); typeManager.getValueType("LIST<STRING>"); expectLastCall().andReturn(valueType); QName name = new QName("aNamespace", "aName"); FieldType fieldType = new FieldTypeImpl(null, valueType, name, Scope.NON_VERSIONED); typeManager.newFieldType(valueType, name, Scope.NON_VERSIONED); expectLastCall().andReturn(fieldType); control.replay(); converter = new AvroConverter(); AvroFieldType avroFieldType = new AvroFieldType(); AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroFieldType.name = avroQName; avroFieldType.scope = AvroScope.NON_VERSIONED; AvroValueType avroValueType = new AvroValueType(); avroValueType.valueType = "LIST<STRING>"; avroFieldType.valueType = avroValueType; converter.convert(avroFieldType, typeManager); // assertEquals(fieldType, converter.convert(avroFieldType)); // assertEquals(avroFieldType, converter.convert(fieldType)); control.verify(); } @Test public void testEmptyRecordType() throws Exception { QName name = new QName("aNamespace", "aName"); SchemaId id = new SchemaIdImpl(UUID.randomUUID()); RecordType recordType = new RecordTypeImpl(id, name); typeManager.newRecordType(id, name); expectLastCall().andReturn(recordType); control.replay(); converter = new AvroConverter(); AvroRecordType avroRecordType = new AvroRecordType(); AvroSchemaId avroSchemaId = new AvroSchemaId(); avroSchemaId.idBytes = ByteBuffer.wrap(id.getBytes()); avroRecordType.id = avroSchemaId; AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroRecordType.name = avroQName; // fieldTypeEntries and supertypes are by default empty instead of null avroRecordType.fieldTypeEntries = new GenericData.Array<AvroFieldTypeEntry>(0, Schema.createArray(AvroFieldTypeEntry.SCHEMA$)); avroRecordType.supertypes = new GenericData.Array<AvroSupertype>(0, Schema.createArray(AvroSupertype.SCHEMA$)); assertEquals(recordType, converter.convert(avroRecordType, typeManager)); assertEquals(avroRecordType, converter.convert(recordType)); control.verify(); } @Test public void testRecordTypeVersion() throws Exception { QName name = new QName("aNamespace", "aName"); SchemaId id = new SchemaIdImpl(UUID.randomUUID()); RecordType recordType = new RecordTypeImpl(id, name); typeManager.newRecordType(id, name); expectLastCall().andReturn(recordType); control.replay(); converter = new AvroConverter(); recordType.setVersion(1L); AvroRecordType avroRecordType = new AvroRecordType(); AvroSchemaId avroSchemaId = new AvroSchemaId(); avroSchemaId.idBytes = ByteBuffer.wrap(id.getBytes()); avroRecordType.id = avroSchemaId; AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroRecordType.name = avroQName; avroRecordType.version = 1L; // fieldTypeEntries and supertypes are by default empty instead of null avroRecordType.fieldTypeEntries = new GenericData.Array<AvroFieldTypeEntry>(0, Schema.createArray(AvroFieldTypeEntry.SCHEMA$)); avroRecordType.supertypes = new GenericData.Array<AvroSupertype>(0, Schema.createArray(AvroSupertype.SCHEMA$)); assertEquals(recordType, converter.convert(avroRecordType, typeManager)); assertEquals(avroRecordType, converter.convert(recordType)); control.verify(); } @Test public void testRecordTypeFieldTypeEntries() throws Exception { QName name = new QName("aNamespace", "aName"); SchemaId recordTypeId = new SchemaIdImpl(UUID.randomUUID()); RecordType recordType = new RecordTypeImpl(recordTypeId, name); typeManager.newRecordType(recordTypeId, name); expectLastCall().andReturn(recordType); SchemaId fieldTypeId1 = new SchemaIdImpl(UUID.randomUUID()); SchemaId fieldTypeId2 = new SchemaIdImpl(UUID.randomUUID()); FieldTypeEntryImpl fieldTypeEntry1 = new FieldTypeEntryImpl(fieldTypeId1, true); FieldTypeEntryImpl fieldTypeEntry2 = new FieldTypeEntryImpl(fieldTypeId2, false); typeManager.newFieldTypeEntry(fieldTypeId1, true); expectLastCall().andReturn(fieldTypeEntry1); typeManager.newFieldTypeEntry(fieldTypeId2, false); expectLastCall().andReturn(fieldTypeEntry2); control.replay(); converter = new AvroConverter(); recordType.addFieldTypeEntry(fieldTypeEntry1); recordType.addFieldTypeEntry(fieldTypeEntry2); AvroRecordType avroRecordType = new AvroRecordType(); AvroSchemaId avroRecordTypeId = new AvroSchemaId(); avroRecordTypeId.idBytes = ByteBuffer.wrap(recordTypeId.getBytes()); avroRecordType.id = avroRecordTypeId; AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroRecordType.name = avroQName; // fieldTypeEntries and supertypes are by default empty instead of null avroRecordType.fieldTypeEntries = new GenericData.Array<AvroFieldTypeEntry>(0, Schema.createArray(AvroFieldTypeEntry.SCHEMA$)); AvroFieldTypeEntry avroFieldTypeEntry = new AvroFieldTypeEntry(); AvroSchemaId avroFieldTypeId1 = new AvroSchemaId(); avroFieldTypeId1.idBytes = ByteBuffer.wrap(fieldTypeId1.getBytes()); avroFieldTypeEntry.id = avroFieldTypeId1; avroFieldTypeEntry.mandatory = true; avroRecordType.fieldTypeEntries.add(avroFieldTypeEntry); Set<AvroFieldTypeEntry> expectedFieldTypeEntries = new HashSet<AvroFieldTypeEntry>(); expectedFieldTypeEntries.add(avroFieldTypeEntry); avroFieldTypeEntry = new AvroFieldTypeEntry(); AvroSchemaId avroFieldTypeId2 = new AvroSchemaId(); avroFieldTypeId2.idBytes = ByteBuffer.wrap(fieldTypeId2.getBytes()); avroFieldTypeEntry.id = avroFieldTypeId2; avroFieldTypeEntry.mandatory = false; avroRecordType.fieldTypeEntries.add(avroFieldTypeEntry); expectedFieldTypeEntries.add(avroFieldTypeEntry); avroRecordType.supertypes = new GenericData.Array<AvroSupertype>(0, Schema.createArray(AvroSupertype.SCHEMA$)); assertEquals(recordType, converter.convert(avroRecordType, typeManager)); AvroRecordType actualAvroRecordType = converter.convert(recordType); List<AvroFieldTypeEntry> fieldTypeEntries = actualAvroRecordType.fieldTypeEntries; assertEquals(2, fieldTypeEntries.size()); Set<AvroFieldTypeEntry> actualFieldTypeEntries = new HashSet<AvroFieldTypeEntry>(); for (AvroFieldTypeEntry entry : fieldTypeEntries) { actualFieldTypeEntries.add(entry); } assertEquals(expectedFieldTypeEntries, actualFieldTypeEntries); control.verify(); } @Test public void testRecordTypeSupertypes() throws Exception { QName name = new QName("aNamespace", "aName"); SchemaId recordTypeId = new SchemaIdImpl(UUID.randomUUID()); RecordType recordType = new RecordTypeImpl(recordTypeId, name); typeManager.newRecordType(recordTypeId, name); expectLastCall().andReturn(recordType); control.replay(); converter = new AvroConverter(); SchemaId supertypeId1 = new SchemaIdImpl(UUID.randomUUID()); recordType.addSupertype(supertypeId1, 1L); SchemaId supertypeId2 = new SchemaIdImpl(UUID.randomUUID()); recordType.addSupertype(supertypeId2, 2L); AvroRecordType avroRecordType = new AvroRecordType(); AvroSchemaId avroRecordTypeId = new AvroSchemaId(); avroRecordTypeId.idBytes = ByteBuffer.wrap(recordTypeId.getBytes()); avroRecordType.id = avroRecordTypeId; AvroQName avroQName = new AvroQName(); avroQName.namespace = "aNamespace"; avroQName.name = "aName"; avroRecordType.name = avroQName; // fieldTypeEntries and supertypes are by default empty instead of null avroRecordType.fieldTypeEntries = new GenericData.Array<AvroFieldTypeEntry>(0, Schema.createArray(AvroFieldTypeEntry.SCHEMA$)); avroRecordType.supertypes = new GenericData.Array<AvroSupertype>(0, Schema.createArray(AvroSupertype.SCHEMA$)); AvroSupertype avroSupertype1 = new AvroSupertype(); AvroSchemaId avroSupertypeId1 = new AvroSchemaId(); avroSupertypeId1.idBytes = ByteBuffer.wrap(supertypeId1.getBytes()); avroSupertype1.recordTypeId = avroSupertypeId1; avroSupertype1.recordTypeVersion = 1L; avroRecordType.supertypes.add(avroSupertype1); Set<AvroSupertype> expectedSupertypes = new HashSet<AvroSupertype>(); expectedSupertypes.add(avroSupertype1); AvroSupertype avroSupertype2 = new AvroSupertype(); AvroSchemaId avroSupertypeId2 = new AvroSchemaId(); avroSupertypeId2.idBytes = ByteBuffer.wrap(supertypeId2.getBytes()); avroSupertype2.recordTypeId = avroSupertypeId2; avroSupertype2.recordTypeVersion = 2L; avroRecordType.supertypes.add(avroSupertype2); expectedSupertypes.add(avroSupertype2); assertEquals(recordType, converter.convert(avroRecordType, typeManager)); AvroRecordType actualAvroRecordType = converter.convert(recordType); List<AvroSupertype> supertypes = actualAvroRecordType.supertypes; assertEquals(2, supertypes.size()); Set<AvroSupertype> actualSupertypes = new HashSet<AvroSupertype>(); for (AvroSupertype entry : supertypes) { actualSupertypes.add(entry); } assertEquals(expectedSupertypes, actualSupertypes); control.verify(); } @Test public void testEmptyRecord() throws Exception { converter = new AvroConverter(); FieldTypes fieldTypesSnapshot = control.createMock(FieldTypes.class); recordFactory.newRecord(); expectLastCall().andReturn(new RecordImpl()).anyTimes(); typeManager.getFieldTypesSnapshot(); expectLastCall().andReturn(fieldTypesSnapshot).anyTimes(); control.replay(); Record record = new RecordImpl(); record.setRecordType(new QName("ns", "recordTypeName"), null); assertEquals(record, converter.convertRecord(converter.convert(record, repository), repository)); assertEquals(converter.convert(record, repository), converter.convert(converter.convertRecord(converter.convert(record, repository), repository), repository)); control.verify(); } @Test public void testRecord() throws Exception { converter = new AvroConverter(); FieldType fieldType = control.createMock(FieldType.class); FieldTypes fieldTypesSnapshot = control.createMock(FieldTypes.class); ValueType valueType = new StringValueType(); IdGenerator idGenerator = new IdGeneratorImpl(); recordFactory.newRecord(); expectLastCall().andReturn(new RecordImpl()).anyTimes(); repository.getIdGenerator(); expectLastCall().andReturn(idGenerator).anyTimes(); typeManager.getFieldTypesSnapshot(); expectLastCall().andReturn(fieldTypesSnapshot).anyTimes(); fieldTypesSnapshot.getFieldType(isA(QName.class)); expectLastCall().andReturn(fieldType).anyTimes(); fieldType.getValueType(); expectLastCall().andReturn(valueType).anyTimes(); typeManager.getValueType("STRING"); expectLastCall().andReturn(valueType).anyTimes(); control.replay(); Record record = new RecordImpl(); RecordId recordId = repository.getIdGenerator().newRecordId(); record.setId(recordId); // Scope.NON_VERSIONED recordType and master record type are the same record.setRecordType(Scope.NON_VERSIONED, new QName("ns", "nvrt"), 1L); record.setRecordType(Scope.VERSIONED, new QName("ns", "vrt"), 2L); record.setRecordType(Scope.VERSIONED_MUTABLE, new QName("ns", "vmrt"), 3L); QName fieldName = new QName("ns", "aName"); record.setField(fieldName, "aValue"); QName fieldName2 = new QName("ns", "aName2"); record.setField(fieldName2, "aValue2"); record.addFieldsToDelete(Arrays.asList(new QName("devnull", "fieldToDelete"))); assertEquals(record, converter.convertRecord(converter.convert(record, repository), repository)); control.verify(); } }