/*
* Copyright © 2014 Cask Data, 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 co.cask.cdap.io;
import co.cask.cdap.api.data.schema.Schema;
import co.cask.cdap.api.data.schema.UnsupportedTypeException;
import co.cask.cdap.api.flow.flowlet.StreamEvent;
import co.cask.cdap.common.io.BinaryDecoder;
import co.cask.cdap.common.io.BinaryEncoder;
import co.cask.cdap.internal.io.ASMDatumWriterFactory;
import co.cask.cdap.internal.io.ASMFieldAccessorFactory;
import co.cask.cdap.internal.io.DatumWriter;
import co.cask.cdap.internal.io.ReflectionDatumReader;
import co.cask.cdap.internal.io.ReflectionDatumWriter;
import co.cask.cdap.internal.io.ReflectionSchemaGenerator;
import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
*
*/
public class ASMDatumCodecTest {
private static final ASMDatumWriterFactory DATUM_WRITER_FACTORY
= new ASMDatumWriterFactory(new ASMFieldAccessorFactory());
/**
*
*/
public static enum TestEnum {
VALUE1, VALUE2, VALUE3, VALUE4
}
private <T> Schema getSchema(TypeToken<T> type) throws UnsupportedTypeException {
return new ReflectionSchemaGenerator().generate(type.getType());
}
private <T> DatumWriter<T> getWriter(TypeToken<T> type) throws UnsupportedTypeException {
Schema schema = getSchema(type);
return DATUM_WRITER_FACTORY.create(type, schema);
}
@Test
public void testShort() throws UnsupportedTypeException, IOException {
TypeToken<Short> type = new TypeToken<Short>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Short> writer = getWriter(type);
writer.encode((short) 3000, new BinaryEncoder(os));
ReflectionDatumReader<Short> reader = new ReflectionDatumReader<>(getSchema(type), type);
short value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals((short) 3000, value);
}
@Test
public void testInt() throws UnsupportedTypeException, IOException {
TypeToken<Integer> type = new TypeToken<Integer>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Integer> writer = getWriter(type);
writer.encode(12234234, new BinaryEncoder(os));
ReflectionDatumReader<Integer> reader = new ReflectionDatumReader<>(getSchema(type), type);
int value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(12234234, value);
}
@Test
public void testDouble() throws UnsupportedTypeException, IOException {
TypeToken<Double> type = new TypeToken<Double>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Double> writer = getWriter(type);
writer.encode(3.14d, new BinaryEncoder(os));
ReflectionDatumReader<Double> reader = new ReflectionDatumReader<>(getSchema(type), type);
double value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(3.14d, value, 0.000001d);
}
@Test
public void testString() throws UnsupportedTypeException, IOException {
TypeToken<String> type = new TypeToken<String>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<String> writer = getWriter(type);
writer.encode("Testing message", new BinaryEncoder(os));
ReflectionDatumReader<String> reader = new ReflectionDatumReader<>(getSchema(type), type);
String value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals("Testing message", value);
}
@Test
public void testUUID() throws UnsupportedTypeException, IOException {
TypeToken<UUID> type = new TypeToken<UUID>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<UUID> writer = getWriter(type);
UUID uuid = UUID.randomUUID();
writer.encode(uuid, new BinaryEncoder(os));
ReflectionDatumReader<UUID> reader = new ReflectionDatumReader<>(getSchema(type), type);
UUID value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(uuid, value);
}
@Test
public void testEnum() throws UnsupportedTypeException, IOException {
TypeToken<TestEnum> type = new TypeToken<TestEnum>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<TestEnum> writer = getWriter(type);
BinaryEncoder encoder = new BinaryEncoder(os);
writer.encode(TestEnum.VALUE1, encoder);
writer.encode(TestEnum.VALUE4, encoder);
writer.encode(TestEnum.VALUE3, encoder);
ReflectionDatumReader<TestEnum> reader = new ReflectionDatumReader<>(getSchema(type), type);
TestEnum value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(TestEnum.VALUE1, value);
value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(TestEnum.VALUE4, value);
value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(TestEnum.VALUE3, value);
}
@Test
public void testPrimitiveArray() throws IOException, UnsupportedTypeException {
TypeToken<int[]> type = new TypeToken<int[]>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
int[] writeValue = {1, 2, 3, 4, -5, -6, -7, -8};
DatumWriter<int[]> writer = getWriter(type);
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<int[]> reader = new ReflectionDatumReader<>(getSchema(type), type);
int[] value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertArrayEquals(writeValue, value);
}
@Test
public void testReferenceArray() throws IOException, UnsupportedTypeException {
TypeToken<String[]> type = new TypeToken<String[]>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
String[] writeValue = new String[] {"1", "2", null, "3"};
DatumWriter<String[]> writer = getWriter(type);
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<String[]> reader = new ReflectionDatumReader<>(getSchema(type), type);
String[] value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertArrayEquals(writeValue, value);
}
@Test
public void testList() throws IOException, UnsupportedTypeException {
TypeToken<List<Long>> type = new TypeToken<List<Long>>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
List<Long> writeValue = ImmutableList.of(1L, 10L, 100L, 1000L);
DatumWriter<List<Long>> writer = getWriter(type);
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<List<Long>> reader = new ReflectionDatumReader<>(getSchema(type), type);
List<Long> value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(writeValue, value);
}
@Test
public void testMap() throws IOException, UnsupportedTypeException {
TypeToken<Map<String, List<String>>> type = new TypeToken<Map<String, List<String>>>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Map<String, List<String>>> writer = getWriter(type);
ImmutableMap<String, List<String>> map = ImmutableMap.<String, List<String>>of("k1", Lists.newArrayList("v1"),
"k2", Lists.newArrayList("v2", null));
writer.encode(map, new BinaryEncoder(os));
ReflectionDatumReader<Map<String, List<String>>> reader =
new ReflectionDatumReader<>(getSchema(type), type);
Assert.assertEquals(map, reader.read(new BinaryDecoder(is), getSchema(type)));
}
@Test
public void testURI() throws IOException, UnsupportedTypeException {
TypeToken<List<URI>> type = new TypeToken<List<URI>>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<List<URI>> writer = getWriter(type);
List<URI> writeValue = ImmutableList.of(URI.create("http://www.abc.com"));
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<List<URI>> reader = new ReflectionDatumReader<>(getSchema(type), type);
Assert.assertEquals(writeValue, reader.read(new BinaryDecoder(is), getSchema(type)));
}
private static class Record {
private int i;
private String s;
private List<String> list;
private TestEnum e;
public Record(int i, String s, List<String> list, TestEnum e) {
this.i = i;
this.s = s;
this.list = list;
this.e = e;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Record record = (Record) o;
return i == record.i && e == record.e && list.equals(record.list) && s.equals(record.s);
}
@Override
public int hashCode() {
return Objects.hashCode(i, s, list, e);
}
}
@Test
public void testRecord() throws IOException, UnsupportedTypeException {
TypeToken<Record> type = new TypeToken<Record>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Record> writer = getWriter(type);
Record writeValue = new Record(10, "testing", ImmutableList.of("a", "b", "c"), TestEnum.VALUE2);
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<Record> reader = new ReflectionDatumReader<>(getSchema(type), type);
Record value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(writeValue, value);
}
@Test
public void testRecordContainer() throws IOException, UnsupportedTypeException {
TypeToken<List<Record>> type = new TypeToken<List<Record>>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<List<Record>> writer = getWriter(type);
List<Record> writeValue = ImmutableList.of(
new Record(10, "testing", ImmutableList.of("a", "b", "c"), TestEnum.VALUE2));
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<List<Record>> reader = new ReflectionDatumReader<>(getSchema(type), type);
List<Record> value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(writeValue, value);
}
@Test
public void testRecordArray() throws IOException, UnsupportedTypeException {
TypeToken<Record[][]> type = new TypeToken<Record[][]>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Record[][]> writer = getWriter(type);
Record[][] writeValue = new Record[][] {{ new Record(10, "testing",
ImmutableList.of("a", "b", "c"), TestEnum.VALUE2)}};
writer.encode(writeValue, new BinaryEncoder(os));
ReflectionDatumReader<Record[][]> reader = new ReflectionDatumReader<>(getSchema(type), type);
Record[][] value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertArrayEquals(writeValue, value);
}
/**
*
*/
public static final class Node {
public int data;
public Node left;
public Node right;
public Node(int data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Node node = (Node) o;
return data == node.data
&& (left != null ? left.equals(node.left) : node.left == null)
&& (right != null ? right.equals(node.right) : node.right == null);
}
@Override
public int hashCode() {
return Objects.hashCode(data, left, right);
}
}
@Test
public void testTree() throws IOException, UnsupportedTypeException {
TypeToken<Node> type = new TypeToken<Node>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<Node> writer = getWriter(type);
Node root = new Node(1, new Node(2, null, new Node(3, null, null)), new Node(4, new Node(5, null, null), null));
writer.encode(root, new BinaryEncoder(os));
ReflectionDatumReader<Node> reader = new ReflectionDatumReader<>(getSchema(type), type);
Node value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(root, value);
}
@Test
public void testStreamEvent() throws IOException, UnsupportedTypeException {
TypeToken<StreamEvent> type = new TypeToken<StreamEvent>() { };
PipedOutputStream os = new PipedOutputStream();
PipedInputStream is = new PipedInputStream(os);
DatumWriter<StreamEvent> writer = getWriter(type);
StreamEvent event = new StreamEvent(ImmutableMap.of("key", "value"),
ByteBuffer.wrap("Testing message".getBytes(Charsets.UTF_8)));
writer.encode(event, new BinaryEncoder(os));
ReflectionDatumReader<StreamEvent> reader = new ReflectionDatumReader<>(getSchema(type), type);
StreamEvent value = reader.read(new BinaryDecoder(is), getSchema(type));
Assert.assertEquals(event.getHeaders(), value.getHeaders());
Assert.assertEquals(event.getBody(), value.getBody());
}
@Ignore
@Test
public void testSpeed() throws UnsupportedTypeException, IOException {
TypeToken<Node> type = new TypeToken<Node>() { };
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
long startTime;
long endTime;
Node writeValue = new Node(1, new Node(2, null, new Node(3, null, null)),
new Node(4, new Node(5, null, null), null));
DatumWriter<Node> writer = getWriter(type);
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
os.reset();
writer.encode(writeValue, new BinaryEncoder(os));
}
endTime = System.nanoTime();
System.out.println("Time spent: " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS));
ReflectionDatumWriter<Node> datumWriter = new ReflectionDatumWriter<>(getSchema(type));
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
os.reset();
datumWriter.encode(writeValue, new BinaryEncoder(os));
}
endTime = System.nanoTime();
System.out.println("Time spent: " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS));
writer = getWriter(type);
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
os.reset();
writer.encode(writeValue, new BinaryEncoder(os));
}
endTime = System.nanoTime();
System.out.println("Time spent: " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS));
datumWriter = new ReflectionDatumWriter<>(getSchema(type));
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
os.reset();
datumWriter.encode(writeValue, new BinaryEncoder(os));
}
endTime = System.nanoTime();
System.out.println("Time spent: " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS));
}
}