summaryrefslogtreecommitdiff
path: root/src/test/java/com/amazon/carbonado/spi
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/amazon/carbonado/spi')
-rw-r--r--src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java115
-rw-r--r--src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java515
-rw-r--r--src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java857
-rw-r--r--src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java571
4 files changed, 2058 insertions, 0 deletions
diff --git a/src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java b/src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java
new file mode 100644
index 0000000..664bf12
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * 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 com.amazon.carbonado.spi;
+
+import java.io.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.lob.*;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+import com.amazon.carbonado.stored.*;
+
+/**
+ * Test case for {@link StorableSerializer}.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestStorableSerializer extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestStorableSerializer.class);
+ }
+
+ private Repository mRepository;
+
+ public TestStorableSerializer(String name) {
+ super(name);
+ }
+
+ protected void setUp() {
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() {
+ mRepository.close();
+ mRepository = null;
+ }
+
+ public void testReadAndWrite() throws Exception {
+ Storage<StorableTestBasic> storage = mRepository.storageFor(StorableTestBasic.class);
+ StorableTestBasic stb = storage.prepare();
+ stb.setId(50);
+ stb.setStringProp("hello");
+ stb.setIntProp(100);
+ stb.setLongProp(999);
+ stb.setDoubleProp(2.718281828d);
+
+ StorableSerializer<StorableTestBasic> serializer =
+ StorableSerializer.forType(StorableTestBasic.class);
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+
+ serializer.write(stb, (DataOutput) dout);
+ dout.flush();
+
+ byte[] bytes = bout.toByteArray();
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
+ DataInputStream din = new DataInputStream(bin);
+
+ StorableTestBasic stb2 = serializer.read(storage, (DataInput) din);
+
+ assertEquals(stb, stb2);
+ }
+
+ /*
+ public void testReadAndWriteLobs() throws Exception {
+ Storage<StorableWithLobs> storage = mRepository.storageFor(StorableWithLobs.class);
+ StorableWithLobs s = storage.prepare();
+ s.setBlobValue(new ByteArrayBlob("Hello Blob".getBytes()));
+ s.setClobValue(new StringClob("Hello Clob"));
+
+ StorableSerializer<StorableWithLobs> serializer =
+ StorableSerializer.forType(StorableWithLobs.class);
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+
+ serializer.write(s, (DataOutput) dout);
+ dout.flush();
+
+ byte[] bytes = bout.toByteArray();
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
+ DataInputStream din = new DataInputStream(bin);
+
+ StorableWithLobs s2 = serializer.read(storage, (DataInput) din);
+
+ assertEquals(s, s2);
+ }
+ */
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java b/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java
new file mode 100644
index 0000000..a65ab21
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/spi/raw/TestDataEncoding.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * 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 com.amazon.carbonado.spi.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link DataEncoder} and {@link DataDecoder}.
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestDataEncoding extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 1000;
+ private static final int LONG_TEST = 10000;
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestDataEncoding.class);
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayCompare(byte[] aa, byte[] ab) {
+ int len = Math.min(aa.length, ab.length);
+ int result = byteArrayCompare(aa, ab, len);
+ if (result == 0 && aa.length != ab.length) {
+ if (aa.length == len) {
+ return -1;
+ }
+ if (ab.length == len) {
+ return 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayCompare(byte[] aa, byte[] ab, int len) {
+ for (int i=0; i<len; i++) {
+ int a = aa[i] & 0xff;
+ int b = ab[i] & 0xff;
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayOrNullCompare(byte[] aa, byte[] ab) {
+ if (aa == null) {
+ if (ab == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (ab == null) {
+ return -1;
+ } else {
+ return byteArrayCompare(aa, ab);
+ }
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayOrNullCompare(byte[] aa, byte[] ab, int len) {
+ if (aa == null) {
+ if (ab == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (ab == null) {
+ return -1;
+ } else {
+ return byteArrayCompare(aa, ab, len);
+ }
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static <C extends Comparable> int compare(C a, C b) {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (b == null) {
+ return -1;
+ } else {
+ return Integer.signum(a.compareTo(b));
+ }
+ }
+
+ private final long mSeed;
+
+ private Random mRandom;
+
+ public TestDataEncoding(String name) {
+ super(name);
+ mSeed = 5399777425345431L;
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_boolean() throws Exception {
+ byte[] bytes = new byte[1];
+ boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ boolean value = mRandom.nextBoolean();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeBoolean(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Boolean() throws Exception {
+ byte[] bytes = new byte[1];
+ Boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Boolean value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = mRandom.nextBoolean();
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeBooleanObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byte() throws Exception {
+ byte[] bytes = new byte[1];
+ byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ byte value = (byte) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeByte(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Byte() throws Exception {
+ byte[] bytes = new byte[2];
+ Byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Byte value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (byte) mRandom.nextInt();
+ assertEquals(2, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeByteObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_short() throws Exception {
+ byte[] bytes = new byte[2];
+ short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ short value = (short) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeShort(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Short() throws Exception {
+ byte[] bytes = new byte[3];
+ Short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Short value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (short) mRandom.nextInt();
+ assertEquals(3, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeShortObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_char() throws Exception {
+ byte[] bytes = new byte[2];
+ char lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ char value = (char) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeChar(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Character() throws Exception {
+ byte[] bytes = new byte[3];
+ Character lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Character value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (char) mRandom.nextInt();
+ assertEquals(3, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeCharacterObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_int() throws Exception {
+ byte[] bytes = new byte[4];
+ int lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ int value = mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeInt(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Integer() throws Exception {
+ byte[] bytes = new byte[5];
+ Integer lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Integer value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = mRandom.nextInt();
+ assertEquals(5, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeIntegerObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_long() throws Exception {
+ byte[] bytes = new byte[8];
+ long lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ long value = mRandom.nextLong();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeLong(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Long() throws Exception {
+ byte[] bytes = new byte[9];
+ Long lastValue = 0L;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Long value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = mRandom.nextLong();
+ assertEquals(9, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeLongObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_float() throws Exception {
+ byte[] bytes = new byte[4];
+ float lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ float value = Float.intBitsToFloat(mRandom.nextInt());
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeFloat(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Float() throws Exception {
+ byte[] bytes = new byte[4];
+ Float lastValue = 0f;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Float value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeFloatObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_double() throws Exception {
+ byte[] bytes = new byte[8];
+ double lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ double value = Double.longBitsToDouble(mRandom.nextLong());
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeDouble(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Double() throws Exception {
+ byte[] bytes = new byte[8];
+ Double lastValue = 0d;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Double value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeDoubleObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_String() throws Exception {
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[DataEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, DataEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, DataDecoder.decodeString(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java b/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java
new file mode 100644
index 0000000..29572f0
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/spi/raw/TestEncodingStrategy.java
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * 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 com.amazon.carbonado.spi.raw;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.cojen.classfile.*;
+import org.cojen.util.*;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.info.*;
+import com.amazon.carbonado.spi.*;
+
+/**
+ * Test case for {@link GenericEncodingStrategy}.
+ * <p>
+ * It generates random selections of properties, encodes with random values,
+ * and checks that the decoding produces the original results. In addition, the
+ * proper ordering of encoded keys is checked.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestEncodingStrategy extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 1000;
+ private static final int LONG_TEST = 10000;
+
+ private static final int ENCODE_OBJECT_ARRAY = 0;
+ private static final int DECODE_OBJECT_ARRAY = 1;
+ private static final int ENCODE_OBJECT_ARRAY_PARTIAL = 2;
+
+ private static final int BOGUS_GENERATION = 99;
+
+ // Make sure BOGUS_GENERATION is not included.
+ private static final int[] GENERATIONS = {-1, 0, 1, 2, 127, 128, 129, Integer.MAX_VALUE};
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestEncodingStrategy.class);
+ }
+
+ private final long mSeed;
+ private final StorableProperty<TestStorable>[] mProperties;
+
+ private Random mRandom;
+
+ public TestEncodingStrategy(String name) {
+ super(name);
+ mSeed = 986184829029842L;
+ Collection<? extends StorableProperty<TestStorable>> properties =
+ StorableIntrospector.examine(TestStorable.class).getAllProperties().values();
+ mProperties = properties.toArray(new StorableProperty[0]);
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_dataEncoding_noProperties() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(0, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_prefix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(5, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_suffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(0, 7, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_prefixAndSuffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(5, 7, generation);
+ }
+ }
+
+ private void test_dataEncoding_noProperties(int prefix, int suffix, int generation)
+ throws Exception
+ {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, 0, 0, prefix, suffix);
+
+ Method[] methods = generateCodecMethods
+ (strategy, new StorableProperty[0], null, generation);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {new Object[0]});
+
+ int generationPrefix;
+ if (generation < 0) {
+ generationPrefix = 0;
+ } else if (generation < 128) {
+ generationPrefix = 1;
+ } else {
+ generationPrefix = 4;
+ }
+
+ assertEquals(encoded.length, prefix + generationPrefix + suffix);
+
+ if (generation >= 0) {
+ if (generationPrefix == 1) {
+ assertEquals(generation, encoded[prefix]);
+ } else {
+ int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+ assertEquals(generation, actualGeneration);
+ }
+ }
+
+ // Decode should not throw an exception.
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+
+ // Generation mismatch should throw an exception.
+ if (generation >= 0) {
+ encoded[prefix] = BOGUS_GENERATION;
+ try {
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ fail();
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CorruptEncodingException) {
+ CorruptEncodingException cee = (CorruptEncodingException) cause;
+ // Make sure error message includes actual generation.
+ assertTrue(cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(0, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_prefix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(5, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_suffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(0, 7, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(5, 7, generation);
+ }
+ }
+
+ /**
+ * @param generation when non-negative, encode a storable layout generation
+ * value in one or four bytes.
+ */
+ private void test_dataEncoding_multipleProperties(int prefix, int suffix, int generation)
+ throws Exception
+ {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, 0, 0, prefix, suffix);
+
+ for (int i=0; i<SHORT_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+
+ Method[] methods = generateCodecMethods(strategy, properties, null, generation);
+ Object[] values = selectPropertyValues(properties);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values});
+
+ int generationPrefix;
+ if (generation < 0) {
+ generationPrefix = 0;
+ } else if (generation < 128) {
+ generationPrefix = 1;
+ } else {
+ generationPrefix = 4;
+ }
+
+ assertTrue(encoded.length > (prefix + generationPrefix + suffix));
+
+ if (generation >= 0) {
+ if (generationPrefix == 1) {
+ assertEquals(generation, encoded[prefix]);
+ } else {
+ int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+ assertEquals(generation, actualGeneration);
+ }
+ }
+
+ if (prefix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = (byte) mRandom.nextInt();
+ }
+ }
+
+ if (suffix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+ }
+ }
+
+ Object[] decodedValues = new Object[values.length];
+ methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+ for (int j=0; j<properties.length; j++) {
+ Object a = values[j];
+ Object b = decodedValues[j];
+ if (properties[j].getType() == byte[].class) {
+ assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) a, (byte[]) b));
+ } else {
+ assertEquals(a, b);
+ }
+ }
+
+ // Generation mismatch should throw an exception.
+ if (generation >= 0) {
+ encoded[prefix] = BOGUS_GENERATION;
+ try {
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ fail();
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CorruptEncodingException) {
+ CorruptEncodingException cee = (CorruptEncodingException) cause;
+ // Make sure error message includes actual generation.
+ assertTrue
+ (cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ }
+
+ public void test_keyEncoding_noProperties() throws Exception {
+ test_keyEncoding_noProperties(0, 0);
+ }
+
+ public void test_keyEncoding_noProperties_prefix() throws Exception {
+ test_keyEncoding_noProperties(5, 0);
+ }
+
+ public void test_keyEncoding_noProperties_suffix() throws Exception {
+ test_keyEncoding_noProperties(0, 7);
+ }
+
+ public void test_keyEncoding_noProperties_prefixAndSuffix() throws Exception {
+ test_keyEncoding_noProperties(5, 7);
+ }
+
+ private void test_keyEncoding_noProperties(int prefix, int suffix) throws Exception {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, prefix, suffix, 0, 0);
+
+ Method[] methods = generateCodecMethods
+ (strategy, new StorableProperty[0], new Direction[0], -1);
+
+ // Encode should return an empty array.
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {new Object[0]});
+ assertEquals(encoded.length, prefix + suffix);
+
+ // Decode should not throw an exception.
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ }
+
+ public void test_keyEncoding_multipleProperties() throws Exception {
+ test_keyEncoding_multipleProperties(0, 0);
+ }
+
+ public void test_keyEncoding_multipleProperties_prefix() throws Exception {
+ test_keyEncoding_multipleProperties(5, 0);
+ }
+
+ public void test_keyEncoding_multipleProperties_suffix() throws Exception {
+ test_keyEncoding_multipleProperties(0, 7);
+ }
+
+ public void test_keyEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+ test_keyEncoding_multipleProperties(5, 7);
+ }
+
+ private void test_keyEncoding_multipleProperties(int prefix, int suffix) throws Exception {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, prefix, suffix, 0, 0);
+
+ for (int i=0; i<MEDIUM_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+ Direction[] directions = selectDirections(properties.length);
+
+ Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+ Object[] values = selectPropertyValues(properties);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values});
+
+ assertTrue(encoded.length > (prefix + suffix));
+
+ // Encode using partial encoding method, but do all
+ // properties. Ensure that the encoding is exactly the same.
+ byte[] encoded2 = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+ .invoke(null, new Object[] {values, 0, properties.length});
+ assertTrue(Arrays.equals(encoded, encoded2));
+
+ if (prefix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = (byte) mRandom.nextInt();
+ }
+ }
+
+ if (suffix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+ }
+ }
+
+ Object[] decodedValues = new Object[values.length];
+ methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+ for (int j=0; j<properties.length; j++) {
+ Object a = values[j];
+ Object b = decodedValues[j];
+ if (properties[j].getType() == byte[].class) {
+ assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) a, (byte[]) b));
+ } else {
+ assertEquals(a, b);
+ }
+ }
+
+ // Now test partial encoding of keys.
+
+ // Clear out random affixes, since we don't need specific values
+ // anymore and it interferes with next test.
+ if (prefix > 0) {
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = 0;
+ }
+ }
+ if (suffix > 0) {
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = 0;
+ }
+ }
+
+ for (int j=0; j<SHORT_TEST; j++) {
+ int start, end;
+ if (properties.length == 1) {
+ start = 0;
+ end = 1;
+ } else {
+ start = mRandom.nextInt(properties.length - 1);
+ // Partial encoding doesn't support zero properties, so
+ // ensure randomly selected stride is more than zero.
+ int stride;
+ do {
+ stride = mRandom.nextInt(properties.length - start);
+ } while (stride == 0);
+ end = start + stride + 1;
+ }
+
+ Object[] partialValues = new Object[end - start];
+ System.arraycopy(values, start, partialValues, 0, partialValues.length);
+
+ byte[] partial = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+ .invoke(null, new Object[] {partialValues, start, end});
+
+ // Partial key must be substring of full key.
+ int searchStart = start == 0 ? 0 : prefix;
+ int index = indexOf(encoded, searchStart, encoded.length - searchStart,
+ partial, 0, partial.length, 0);
+
+ if (start == 0) {
+ assertEquals(0, index);
+ } else {
+ assertTrue(index > 0);
+ }
+
+ if (properties.length == 1) {
+ break;
+ }
+ }
+ }
+ }
+
+ public void test_keyEncoding_ordering() throws Exception {
+ GenericEncodingStrategy strategy =
+ new GenericEncodingStrategy(TestStorable.class, null, 0, 0, 0, 0);
+
+ for (int i=0; i<MEDIUM_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+ Direction[] directions = selectDirections(properties.length);
+ Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+
+ Object[] values_1 = selectPropertyValues(properties);
+ byte[] encoded_1 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values_1});
+
+ Object[] values_2 = selectPropertyValues(properties);
+ byte[] encoded_2 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values_2});
+
+ int byteOrder = TestDataEncoding.byteArrayCompare(encoded_1, encoded_2);
+ int valueOrder = compareValues(properties, directions, values_1, values_2);
+
+ assertEquals(valueOrder, byteOrder);
+ }
+ }
+
+ private int compareValues(StorableProperty<?>[] properties, Direction[] directions,
+ Object[] values_1, Object[] values_2) {
+ int length = directions.length;
+ for (int i=0; i<length; i++) {
+ StorableProperty<?> property = properties[i];
+ Direction direction = directions[i];
+
+ Object value_1 = values_1[i];
+ Object value_2 = values_2[i];
+
+ int result;
+ if (property.getType() == byte[].class) {
+ result = TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) value_1, (byte[]) value_2);
+ } else {
+ if (value_1 == null) {
+ if (value_2 == null) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ } else if (value_2 == null) {
+ result = -1;
+ } else {
+ result = Integer.signum(((Comparable) value_1).compareTo(value_2));
+ }
+ }
+
+ if (result != 0) {
+ if (direction == Direction.DESCENDING) {
+ result = -result;
+ }
+ return result;
+ }
+ }
+
+ return 0;
+ }
+
+ // Method taken from String class and modified a bit.
+ private static int indexOf(byte[] source, int sourceOffset, int sourceCount,
+ byte[] target, int targetOffset, int targetCount,
+ int fromIndex) {
+ if (fromIndex >= sourceCount) {
+ return (targetCount == 0 ? sourceCount : -1);
+ }
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+ if (targetCount == 0) {
+ return fromIndex;
+ }
+
+ byte first = target[targetOffset];
+ int max = sourceOffset + (sourceCount - targetCount);
+
+ for (int i = sourceOffset + fromIndex; i <= max; i++) {
+ // Look for first byte.
+ if (source[i] != first) {
+ while (++i <= max && source[i] != first);
+ }
+
+ // Found first byte, now look at the rest of v2
+ if (i <= max) {
+ int j = i + 1;
+ int end = j + targetCount - 1;
+ for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++);
+
+ if (j == end) {
+ // Found whole string.
+ return i - sourceOffset;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * First method is the encoder, second is the decoder. Both methods are
+ * static. Encoder accepts an object array of property values, and it
+ * returns a byte array. Decoder accepts an object array to receive
+ * property values, an encoded byte array, and it returns void.
+ *
+ * <p>If generating key encoding and the property count is more than zero,
+ * then third element in array is a key encoder that supports partial
+ * encoding. In addition to the object array argument, it also accepts an
+ * int start and an int end argument. The start of the range is inclusive,
+ * and the end is exclusive.
+ *
+ * @param directions when supplied, build key encoding/decoding. Otherwise,
+ * build data encoding/decoding.
+ * @param generation when non-negative, encode a storable layout generation
+ * value in one or four bytes.
+ */
+ private Method[] generateCodecMethods(GenericEncodingStrategy strategy,
+ StorableProperty<TestStorable>[] properties,
+ Direction[] directions,
+ int generation)
+ throws Exception
+ {
+ ClassInjector ci = ClassInjector.create(TestStorable.class.getName(), null);
+ ClassFile cf = new ClassFile(ci.getClassName());
+ cf.markSynthetic();
+ cf.setTarget("1.5");
+
+ cf.addDefaultConstructor();
+
+ TypeDesc byteArrayType = TypeDesc.forClass(byte[].class);
+ TypeDesc objectArrayType = TypeDesc.forClass(Object[].class);
+
+ // Build encode method.
+ MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "encode", byteArrayType,
+ new TypeDesc[] {objectArrayType});
+ CodeBuilder b = new CodeBuilder(mi);
+ LocalVariable encodedVar;
+ if (directions != null) {
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ encodedVar =
+ strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false, null, null);
+ } else {
+ encodedVar = strategy.buildDataEncoding
+ (b, properties, b.getParameter(0), null, false, generation);
+ }
+ b.loadLocal(encodedVar);
+ b.returnValue(byteArrayType);
+
+ // Build decode method.
+ mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "decode", null,
+ new TypeDesc[] {objectArrayType, byteArrayType});
+ b = new CodeBuilder(mi);
+ if (directions != null) {
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ strategy.buildKeyDecoding
+ (b, ordered, b.getParameter(0), null, false, b.getParameter(1));
+ } else {
+ strategy.buildDataDecoding
+ (b, properties, b.getParameter(0), null, false,
+ generation, null, b.getParameter(1));
+ }
+ b.returnVoid();
+
+ if (directions != null && properties.length > 0) {
+ // Build encode partial key method.
+ mi = cf.addMethod
+ (Modifiers.PUBLIC_STATIC, "encodePartial", byteArrayType,
+ new TypeDesc[] {objectArrayType, TypeDesc.INT, TypeDesc.INT});
+ b = new CodeBuilder(mi);
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ encodedVar =
+ strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false,
+ b.getParameter(1), b.getParameter(2));
+ b.loadLocal(encodedVar);
+ b.returnValue(byteArrayType);
+ }
+
+ Class<?> clazz = ci.defineClass(cf);
+
+ Method encode = clazz.getMethod("encode", Object[].class);
+ Method decode = clazz.getMethod("decode", Object[].class, byte[].class);
+ Method encodePartial = null;
+ if (directions != null && properties.length > 0) {
+ encodePartial = clazz.getMethod("encodePartial", Object[].class, int.class, int.class);
+ }
+
+ Method[] methods = new Method[3];
+ methods[ENCODE_OBJECT_ARRAY] = encode;
+ methods[DECODE_OBJECT_ARRAY] = decode;
+ methods[ENCODE_OBJECT_ARRAY_PARTIAL] = encodePartial;
+
+ return methods;
+ }
+
+ private StorableProperty<TestStorable>[] selectProperties(int minCount, int maxCount) {
+ int length = (minCount == maxCount) ? minCount
+ : (mRandom.nextInt(maxCount - minCount + 1) + minCount);
+
+ StorableProperty<TestStorable>[] selection = new StorableProperty[length];
+
+ for (int i=length; --i>=0; ) {
+ selection[i] = mProperties[mRandom.nextInt(mProperties.length)];
+ }
+
+ return selection;
+ }
+
+ private Direction[] selectDirections(int count) {
+ Direction[] directions = new Direction[count];
+
+ for (int i=count; --i>=0; ) {
+ Direction dir;
+ switch (mRandom.nextInt(3)) {
+ default:
+ dir = Direction.UNSPECIFIED;
+ break;
+ case 1:
+ dir = Direction.ASCENDING;
+ break;
+ case 2:
+ dir = Direction.DESCENDING;
+ break;
+ }
+ directions[i] = dir;
+ }
+
+ return directions;
+ }
+
+ private OrderedProperty<TestStorable>[] makeOrderedProperties
+ (StorableProperty<TestStorable>[] properties, Direction[] directions) {
+
+ int length = properties.length;
+ OrderedProperty<TestStorable>[] ordered = new OrderedProperty[length];
+ for (int i=length; --i>=0; ) {
+ ordered[i] = OrderedProperty.get(properties[i], directions[i]);
+ }
+
+ return ordered;
+ }
+
+ /**
+ * Returns an array of the same size with randomly selected values filled
+ * in that match the property type.
+ */
+ private Object[] selectPropertyValues(StorableProperty<?>[] properties) {
+ int length = properties.length;
+ Object[] values = new Object[length];
+
+ for (int i=length; --i>=0; ) {
+ StorableProperty<?> property = properties[i];
+ TypeDesc type = TypeDesc.forClass(property.getType());
+
+ Object value;
+
+ if (property.isNullable() && mRandom.nextInt(100) == 0) {
+ value = null;
+ } else {
+ TypeDesc prim = type.toPrimitiveType();
+ if (prim != null) {
+ switch (prim.getTypeCode()) {
+ case TypeDesc.BOOLEAN_CODE: default:
+ value = mRandom.nextBoolean();
+ break;
+ case TypeDesc.CHAR_CODE:
+ value = (char) mRandom.nextInt();
+ break;
+ case TypeDesc.FLOAT_CODE:
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ break;
+ case TypeDesc.DOUBLE_CODE:
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ break;
+ case TypeDesc.BYTE_CODE:
+ value = (byte) mRandom.nextInt();
+ break;
+ case TypeDesc.SHORT_CODE:
+ value = (short) mRandom.nextInt();
+ break;
+ case TypeDesc.INT_CODE:
+ value = mRandom.nextInt();
+ break;
+ case TypeDesc.LONG_CODE:
+ value = mRandom.nextLong();
+ break;
+ }
+ } else if (type == TypeDesc.STRING) {
+ int len = mRandom.nextInt(100);
+ StringBuilder sb = new StringBuilder(len);
+ for (int j=0; j<len; j++) {
+ sb.append((char) mRandom.nextInt());
+ }
+ value = sb.toString();
+ } else {
+ int len = mRandom.nextInt(100);
+ byte[] bytes = new byte[len];
+ for (int j=0; j<len; j++) {
+ bytes[j] = (byte) mRandom.nextInt();
+ }
+ value = bytes;
+ }
+ }
+
+ values[i] = value;
+ }
+
+ return values;
+ }
+
+ /**
+ * Just a collection of storable properties. At least one property defined
+ * per supported type.
+ */
+ @PrimaryKey("byte") // I don't really care what the primary key is.
+ public static interface TestStorable extends Storable {
+ byte getByte();
+
+ void setByte(byte b);
+
+ Byte getByteObj();
+
+ void setByteObj(Byte b);
+
+ @Nullable
+ Byte getNullableByteObj();
+
+ void setNullableByteObj(Byte b);
+
+ short getShort();
+
+ void setShort(short s);
+
+ Short getShortObj();
+
+ void setShortObj(Short s);
+
+ @Nullable
+ Short getNullableShortObj();
+
+ void setNullableShortObj(Short s);
+
+ char getChar();
+
+ void setChar(char c);
+
+ Character getCharacterObj();
+
+ void setCharacterObj(Character c);
+
+ @Nullable
+ Character getNullableCharacterObj();
+
+ void setNullableCharacterObj(Character c);
+
+ int getInt();
+
+ void setInt(int i);
+
+ Integer getIntegerObj();
+
+ void setIntegerObj(Integer obj);
+
+ @Nullable
+ Integer getNullableIntegerObj();
+
+ void setNullableIntegerObj(Integer obj);
+
+ long getLong();
+
+ void setLong(long i);
+
+ Long getLongObj();
+
+ void setLongObj(Long i);
+
+ @Nullable
+ Long getNullableLongObj();
+
+ void setNullableLongObj(Long i);
+
+ float getFloat();
+
+ void setFloat(float f);
+
+ Float getFloatObj();
+
+ void setFloatObj(Float f);
+
+ @Nullable
+ Float getNullableFloatObj();
+
+ void setNullableFloatObj(Float f);
+
+ double getDouble();
+
+ void setDouble(double d);
+
+ Double getDoubleObj();
+
+ void setDoubleObj(Double d);
+
+ @Nullable
+ Double getNullableDoubleObj();
+
+ void setNullableDoubleObj(Double d);
+
+ byte[] getByteArray();
+
+ void setByteArray(byte[] b);
+
+ @Nullable
+ byte[] getNullableByteArray();
+
+ void setNullableByteArray(byte[] b);
+
+ String getString();
+
+ void setString(String s);
+
+ @Nullable
+ String getNullableString();
+
+ void setNullableString(String s);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java b/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java
new file mode 100644
index 0000000..246e6fe
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/spi/raw/TestKeyEncoding.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * 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 com.amazon.carbonado.spi.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link KeyEncoder} and {@link KeyDecoder}.
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestKeyEncoding extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 1000;
+ private static final int LONG_TEST = 10000;
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestKeyEncoding.class);
+ }
+
+ private final long mSeed;
+
+ private Random mRandom;
+
+ public TestKeyEncoding(String name) {
+ super(name);
+ mSeed = 5399777425345431L;
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_booleanDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ boolean value = mRandom.nextBoolean();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeBooleanDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_BooleanDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ Boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Boolean value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = mRandom.nextBoolean();
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeBooleanObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ byte value = (byte) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeByteDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_ByteDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ Byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Byte value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (byte) mRandom.nextInt();
+ assertEquals(2, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeByteObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_shortDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ short value = (short) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeShortDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_ShortDesc() throws Exception {
+ byte[] bytes = new byte[3];
+ Short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Short value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (short) mRandom.nextInt();
+ assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeShortObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_charDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ char lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ char value = (char) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeCharDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_CharacterDesc() throws Exception {
+ byte[] bytes = new byte[3];
+ Character lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Character value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (char) mRandom.nextInt();
+ assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeCharacterObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_intDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ int lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ int value = mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeIntDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_IntegerDesc() throws Exception {
+ byte[] bytes = new byte[5];
+ Integer lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Integer value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = mRandom.nextInt();
+ assertEquals(5, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeIntegerObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_longDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ long lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ long value = mRandom.nextLong();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeLongDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_LongDesc() throws Exception {
+ byte[] bytes = new byte[9];
+ Long lastValue = 0L;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Long value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = mRandom.nextLong();
+ assertEquals(9, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeLongObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_floatDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ float lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ float value = Float.intBitsToFloat(mRandom.nextInt());
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeFloatDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_FloatDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ Float lastValue = 0f;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Float value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeFloatObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_doubleDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ double lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ double value = Double.longBitsToDouble(mRandom.nextLong());
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeDoubleDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_DoubleDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ Double lastValue = 0d;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Double value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeDoubleObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_String() throws Exception {
+ String lastValue = null;
+ byte[] lastBytes = null;
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeString(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+
+ if (lastBytes != null) {
+ int sgn = TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_StringDesc() throws Exception {
+ String lastValue = null;
+ byte[] lastBytes = null;
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeStringDesc(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+
+
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteArray() throws Exception {
+ byte[] lastValue = null;
+ byte[] lastBytes = null;
+ byte[][] ref = new byte[1][];
+ for (int i=0; i<LONG_TEST; i++) {
+ byte[] value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length = mRandom.nextInt(4000);
+ value = new byte[length];
+ for (int j=0; j<length; j++) {
+ value[j] = (byte) mRandom.nextInt();
+ }
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decode(bytes, 0, ref));
+ if (ref[0] == null) {
+ assertEquals(value, null);
+ } else if (value == null) {
+ assertEquals(value, ref[0]);
+ } else {
+ assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+ }
+
+ if (lastBytes != null) {
+ int sgn = TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteArrayDesc() throws Exception {
+ byte[] lastValue = null;
+ byte[] lastBytes = null;
+ byte[][] ref = new byte[1][];
+ for (int i=0; i<LONG_TEST; i++) {
+ byte[] value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length = mRandom.nextInt(4000);
+ value = new byte[length];
+ for (int j=0; j<length; j++) {
+ value[j] = (byte) mRandom.nextInt();
+ }
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeDesc(bytes, 0, ref));
+ if (ref[0] == null) {
+ assertEquals(value, null);
+ } else if (value == null) {
+ assertEquals(value, ref[0]);
+ } else {
+ assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+ }
+
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+}