package tap.core.io.avro;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.avro.Schema;
import org.apache.avro.Schema.Field;
import org.apache.avro.Schema.Field.Order;
import org.apache.avro.generic.GenericData;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.DecoderFactory;
import tap.core.BinaryKeyComparator;
import tap.core.io.BinaryKey;
import tap.core.io.Bytes;
import tap.core.io.SortOrder;
import tap.core.io.Types;
public class BinaryKeySerializationTests {
private static final Schema STRING = Schema.create(Schema.Type.STRING);
private static final Schema INT = Schema.create(Schema.Type.INT);
private Schema schema_asc, schema_desc;
@Before
public void setup() {
List<Field> asc = new ArrayList<Field>();
asc.add(new Field("name", STRING, null, null, Order.ASCENDING));
asc.add(new Field("age", INT, null, null, Order.ASCENDING));
List<Field> desc = new ArrayList<Field>();
desc.add(new Field("name", STRING, null, null, Order.DESCENDING));
desc.add(new Field("age", INT, null, null, Order.DESCENDING));
schema_asc = Schema.createRecord(asc);
schema_desc = Schema.createRecord(desc);
// set age as a sort field
schema_asc.getField("age").addProp("x-sort", "true");
schema_desc.getField("age").addProp("x-sort", "true");
}
private BinaryKey encode(String name, int age, Schema schema) throws IOException {
BinaryKey key = new BinaryKey();
key.setSchema(schema);
key.setField("name", name);
key.setField("age", age);
return key;
}
private BinaryKey decode(byte[] bytes) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
BinaryKeyDatumReader reader = new BinaryKeyDatumReader();
Decoder decoder = DecoderFactory.get().directBinaryDecoder(in, null);
return reader.read(null, decoder);
}
@Test
public void writeRecord() throws IOException {
BinaryKey key = encode("john", 30, schema_asc);
Assert.assertEquals(19, key.getLength());
byte[] bytes = key.getBuffer();
// first 4 bytes is length of key written asc
int dataSize = Bytes.toInt(bytes, 0, SortOrder.ASCENDING);
Assert.assertEquals(11, dataSize);
// next 4 bytes is group length (name)
int groupLength = Bytes.toInt(bytes, 4, SortOrder.ASCENDING);
Assert.assertEquals(6, groupLength);
// string tag
Assert.assertEquals(Types.STRING.asc(), bytes[8] & 0xff);
// name
byte[] name = { 'j', 'o', 'h', 'n', Bytes.TERM };
Assert.assertTrue(Bytes.compare(name, 0, 5, bytes, 9, 5) == 0);
// int tag
Assert.assertEquals(Types.INT.asc(), bytes[14] & 0xff);
// age
int age = Bytes.toInt(bytes, 15, SortOrder.ASCENDING);
Assert.assertEquals(30, age);
}
@Test
public void writeRecordDesc() throws IOException {
BinaryKey key = encode("john", 30, schema_desc);
Assert.assertEquals(19, key.getLength());
byte[] bytes = key.getBuffer();
// first 4 bytes is length of key written asc
int dataSize = Bytes.toInt(bytes, 0, SortOrder.ASCENDING);
Assert.assertEquals(11, dataSize);
// next 4 bytes is group length (name)
int groupLength = Bytes.toInt(bytes, 4, SortOrder.ASCENDING);
Assert.assertEquals(6, groupLength);
// string tag
Assert.assertEquals(Types.STRING.desc(), bytes[8] & 0xff);
// name desc
byte[] name = {
(byte) Bytes.UBYTE_MAX_VALUE - 'j',
(byte) Bytes.UBYTE_MAX_VALUE - 'o',
(byte) Bytes.UBYTE_MAX_VALUE - 'h',
(byte) Bytes.UBYTE_MAX_VALUE - 'n',
(byte) Bytes.UBYTE_MAX_VALUE - Bytes.TERM };
Assert.assertTrue(Bytes.compare(name, 0, 5, bytes, 9, 5) == 0);
// int tag
Assert.assertEquals(Types.INT.desc(), bytes[14] & 0xff);
// age
int age = Bytes.toInt(bytes, 15, SortOrder.DESCENDING);
Assert.assertEquals(30, age);
}
@Test
public void compareAsc() throws IOException {
BinaryKeyComparator comparator = new BinaryKeyComparator();
BinaryKey k1 = encode("john", 30, schema_asc);
BinaryKey k2 = encode("john", 31, schema_asc);
byte[] b1 = k1.getBuffer();
byte[] b2 = k2.getBuffer();
int b1_length = k1.getLength();
int b2_length = k2.getLength();
// b1 == b1
int result = comparator.compare(b1, 0, b1_length, b1, 0, b1_length);
Assert.assertEquals(0, result);
// b1 < b2
result = comparator.compare(b1, 0, b1_length, b2, 0, b2_length);
Assert.assertTrue(result < 0);
// b2 > b1
result = comparator.compare(b2, 0, b2_length, b1, 0, b1_length);
Assert.assertTrue(result > 0);
}
@Test
public void compareDesc() throws IOException {
BinaryKeyComparator comparator = new BinaryKeyComparator();
BinaryKey k1 = encode("john", 30, schema_desc);
BinaryKey k2 = encode("john", 31, schema_desc);
byte[] b1 = k1.getBuffer();
byte[] b2 = k2.getBuffer();
int b1_length = k1.getLength();
int b2_length = k2.getLength();
// b1 == b1
int result = comparator.compare(b1, 0, b1_length, b1, 0, b1_length);
Assert.assertEquals(0, result);
// b1 > b2
result = comparator.compare(b1, 0, b1_length, b2, 0, b2_length);
Assert.assertTrue(result > 0);
// b2 < b1
result = comparator.compare(b2, 0, b2_length, b1, 0, b1_length);
Assert.assertTrue(result < 0);
}
@Test
public void readBinaryKey() throws IOException {
BinaryKey original = encode("john", 30, schema_asc);
BinaryKey key = decode(original.getBuffer());
Assert.assertEquals(19, key.getLength());
Assert.assertNotSame(key.getBuffer(), original.getBuffer());
Assert.assertEquals(0, Bytes.compare(
key.getBuffer(), 0, key.getLength(),
original.getBuffer(), 0, original.getLength()));
Assert.assertEquals(11, key.keyBytesLength());
Assert.assertEquals(6, key.groupBytesLength());
}
}