/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 gobblin.recordaccess;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.apache.avro.Schema;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.io.EncoderFactory;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class AvroGenericRecordAccessorTest {
private Schema recordSchema;
private GenericRecord record;
private AvroGenericRecordAccessor accessor;
@BeforeMethod
public void initRecord() throws IOException {
recordSchema =
new Schema.Parser().parse(getClass().getClassLoader().getResourceAsStream("converter/fieldPickInput.avsc"));
record = new GenericData.Record(recordSchema);
setRequiredRecordFields(record);
accessor = new AvroGenericRecordAccessor(record);
}
@AfterMethod
public void serializeRecord(ITestResult result)
throws IOException {
if (result.isSuccess() && result.getThrowable() == null) {
/* Serialize the GenericRecord; this can catch issues in set() that the underlying GenericRecord
* may not catch until serialize time
*/
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(recordSchema);
ByteArrayOutputStream bOs = new ByteArrayOutputStream();
BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(bOs, null);
datumWriter.write(record, encoder);
encoder.flush();
bOs.flush();
Assert.assertTrue(bOs.toByteArray().length > 0);
}
}
@Test
public void testSuccessfulSetAndGet() {
accessor.set("name", "foo");
accessor.set("favorite_number", 2);
accessor.set("last_modified", 100L);
Assert.assertEquals(accessor.getAsString("name"), "foo");
Assert.assertEquals(accessor.getAsInt("favorite_number").intValue(), 2);
Assert.assertEquals(accessor.getAsLong("last_modified").longValue(), 100L);
}
@Test
public void testParsedRecordGet()
throws IOException {
updateRecordFromTestResource(ACCESSOR_RESOURCE_NAME);
Assert.assertEquals(accessor.getAsString("name"), "testName");
Assert.assertNull(accessor.getAsInt("favorite_number"));
Assert.assertNull(accessor.getAsString("favorite_color"));
Assert.assertEquals(accessor.getAsLong("last_modified").longValue(), 13L);
Assert.assertEquals(accessor.getAsLong("created").longValue(), 14L);
}
@Test
public void testParsedRecordManipulation()
throws IOException {
updateRecordFromTestResource(ACCESSOR_RESOURCE_NAME);
accessor.set("name", "newName");
Assert.assertEquals(accessor.getAsString("name"), "newName");
}
@Test
public void testGetValueFromArray() throws IOException {
setAccessorToRecordWithArrays();
Assert.assertEquals(accessor.getAsString("nestedRecords.1.fieldToEncrypt"), "val1");
}
@Test
public void testSetValueFromArray() throws IOException {
setAccessorToRecordWithArrays();
accessor.set("nestedRecords.1.fieldToEncrypt", "myNewVal");
Assert.assertEquals(accessor.getAsString("nestedRecords.1.fieldToEncrypt"), "myNewVal");
}
@Test
public void testGetMultiValue() throws IOException {
setAccessorToRecordWithArrays();
Map<String, String> fields = accessor.getMultiAsString("nestedRecords.*.fieldToEncrypt");
Assert.assertEquals(fields.size(), 3);
Assert.assertEquals(fields.get("nestedRecords.0.fieldToEncrypt"), "val0");
Assert.assertEquals(fields.get("nestedRecords.1.fieldToEncrypt"), "val1");
Assert.assertEquals(fields.get("nestedRecords.2.fieldToEncrypt"), "val2");
}
@Test(expectedExceptions = FieldDoesNotExistException.class)
public void testSetNonexistentField() {
accessor.set("doesnotexist", "someval");
}
@Test(expectedExceptions = FieldDoesNotExistException.class)
public void testSetNonexistentNestedField() {
accessor.set("subrecord.doesnotexist", "someval");
}
@Test(expectedExceptions = IncorrectTypeException.class)
public void setBadTypePrimitive() {
accessor.set("name", 5L);
}
@Test(expectedExceptions = IncorrectTypeException.class)
public void setBadTypeUnion() {
accessor.set("favorite_color", 0L);
}
@Test(expectedExceptions = IncorrectTypeException.class)
public void getBadType() {
accessor.getAsLong("name");
}
@Test
public void testNestedSetAndGet()
throws IOException {
updateRecordFromTestResource(NESTED_RESOURCE_NAME);
Assert.assertEquals(accessor.getAsString("address.city"), "Mountain view");
accessor.set("address.city", "foobar");
Assert.assertEquals(accessor.getAsString("address.city"), "foobar");
}
@Test
public void setFieldToNull() {
setRequiredRecordFields(record);
accessor.setToNull("favorite_color");
// afterTest serialization methods should ensure this works
}
private static void setRequiredRecordFields(GenericRecord record) {
record.put("name", "validName");
record.put("last_modified", 0L);
record.put("favorite_number", 0);
record.put("date_of_birth", 0L);
record.put("created", 0L);
}
private void updateRecordFromTestResource(String resourceName)
throws IOException {
recordSchema = new Schema.Parser().parse(
getClass().getClassLoader().getResourceAsStream(resourceName + ".avsc")
);
DatumReader<GenericRecord> reader = new GenericDatumReader<>(recordSchema);
DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(
new File(getClass().getClassLoader().getResource(resourceName + ".avro").getPath()), reader);
Assert.assertTrue(dataFileReader.hasNext());
record = dataFileReader.next(record);
accessor = new AvroGenericRecordAccessor(record);
}
private void setAccessorToRecordWithArrays()
throws IOException {
updateRecordFromTestResource("converter/record_with_arrays");
}
private static final String ACCESSOR_RESOURCE_NAME = "converter/fieldPickInput";
private static final String NESTED_RESOURCE_NAME = "converter/nested";
}