/* * 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.hbaseindex; import java.io.IOException; import java.math.BigDecimal; import org.apache.hadoop.hbase.util.Bytes; import org.junit.Test; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Various tests that check whether the comparison of the binary representations * of values gives the expected result. * * Actually we are testing Orderly here, which is the underlying library used for encoding/decoding index row keys. */ public class ByteComparisonTest { @Test public void testSignedIntegerCompare() throws Exception { int[] testNumbers = { Integer.MIN_VALUE, -1000, 0, 1000, Integer.MAX_VALUE}; for (int n1 : testNumbers) { byte[] n1Bytes = toSortableBytes(n1); for (int n2 : testNumbers) { byte[] n2Bytes = toSortableBytes(n2); int cmp = Bytes.compareTo(n1Bytes, n2Bytes); if (cmp == 0) { if (n1 != n2) { System.out.println(n1 + " = " + n2); assertTrue(n1 == n2); } } else if (cmp < 0) { if (!(n1 < n2)) { System.out.println(n1 + " < " + n2); assertTrue(n1 < n2); } } else if (cmp > 0) { if (!(n1 > n2)) { System.out.println(n1 + " > " + n2); assertTrue(n1 > n2); } } } } } @Test public void testSignedFloatCompare() throws Exception { float[] testNumbers = { Float.NEGATIVE_INFINITY, Float.MIN_VALUE, -Float.MIN_VALUE / 2, -1000f, /* this is intended to be a subnormal number */ -0.000000000000000000000000000000000000000000001f, -0f, 0f, 0.000000000000000000000000000000000000000000001f, Float.MIN_NORMAL, 1000f, Float.MAX_VALUE / 2, Float.MAX_VALUE, Float.POSITIVE_INFINITY}; for (float n1 : testNumbers) { byte[] n1Bytes = toSortableBytes(n1); for (float n2 : testNumbers) { byte[] n2Bytes = toSortableBytes(n2); int cmp = Bytes.compareTo(n1Bytes, n2Bytes); if (cmp == 0) { assertTrue(n1 == n2); } else if (cmp < 0) { if (!(n1 < n2) && !(n1 == -0f && n2 == 0f)) { System.out.println(n1 + " > " + n2); assertTrue(n1 < n2); } } else if (cmp > 0) { if (!(n1 > n2) && !(n1 == 0f && n2 == -0f)) { System.out.println(n1 + " > " + n2); assertTrue(n1 > n2); } } } } } @Test public void testUtf8StringCompare() throws Exception { StringIndexFieldDefinition fieldDef = new StringIndexFieldDefinition("foobar"); byte[] string1 = fieldDef.asRowKey().serialize(Bytes.toBytes("\u00EAtre")); byte[] string2 = fieldDef.asRowKey().serialize(Bytes.toBytes("heureux")); assertTrue(Bytes.compareTo(string1, string2) > 0); } private byte[] toSortableBytes(int value) throws IOException { IntegerIndexFieldDefinition fieldDef = new IntegerIndexFieldDefinition("foobar"); return fieldDef.asRowKey().serialize(value); } private byte[] toSortableBytes(float value) throws IOException { FloatIndexFieldDefinition fieldDef = new FloatIndexFieldDefinition("foobar"); return fieldDef.asRowKey().serialize(value); } private byte[] toSortableBytes(BigDecimal value) throws IOException { DecimalIndexFieldDefinition fieldDef = new DecimalIndexFieldDefinition("foobar"); return fieldDef.asRowKey().serialize(value); } @Test public void testDecimalCompare() throws Exception { String[] testNumbers = {"-99999999999999999999999999999999999999999999999999999999999999999999999999999999", "-10.000000000000000000000000000000000000000000000000000000000000000000000000000000002", "-10.000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0", "5.5", "55.5", "0.1E16383"}; BigDecimal[] testDecimals = new BigDecimal[testNumbers.length]; for (int i = 0; i < testDecimals.length; i++) { testDecimals[i] = new BigDecimal(testNumbers[i]); } for (BigDecimal d1 : testDecimals) { for (BigDecimal d2 : testDecimals) { byte[] b1 = toSortableBytes(d1); byte[] b2 = toSortableBytes(d2); int cmp = Bytes.compareTo(b1, b2); if (cmp == 0) { if (!d1.equals(d2)) { System.out.println(d1 + " == " + d2); fail(); } } else if (cmp < 0) { if (!(d1.compareTo(d2) < 0)) { System.out.println(d1 + " < " + d2); fail(); } } else if (cmp > 0) { if (!(d1.compareTo(d2) > 0)) { System.out.println(d1 + " > " + d2); fail(); } } } } } /** * Code to print out a complete bit representation of a byte, * including leading zeros. * * <p>Copied from * http://manniwood.wordpress.com/2009/10/21/javas-long-tobinarystringlong-l-come-on-guys/ */ private String toBinaryString(byte val) { StringBuilder sb = new StringBuilder(8); for (int i = 7; i >= 0; i--) { sb.append((testBit(val, i) == 0) ? '0' : '1'); } return sb.toString(); } byte testBit(byte val, int bitToTest) { val >>= bitToTest; val &= 1; return val; } private String toBinaryString(byte[] values) { StringBuilder sb = new StringBuilder(values.length * 8); for (byte value : values) { sb.append(toBinaryString(value)); } return sb.toString(); } }