/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.bind.tuple.test; import static org.junit.Assert.fail; import java.math.BigDecimal; import java.math.BigInteger; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sleepycat.bind.tuple.TupleOutput; /** * @author Mark Hayes */ public class TupleOrderingTest { private TupleOutput out; private byte[] prevBuf; @Before public void setUp() { out = new TupleOutput(); prevBuf = null; } @After public void tearDown() { /* Ensure that GC can cleanup. */ out = null; prevBuf = null; } /** * Each tuple written must be strictly less than (by comparison of bytes) * the tuple written just before it. The check() method compares bytes * just written to those written before the previous call to check(). */ private void check() { check(-1); } private void check(int dataIndex) { byte[] buf = new byte[out.size()]; System.arraycopy(out.getBufferBytes(), out.getBufferOffset(), buf, 0, buf.length); if (prevBuf != null) { int errOffset = -1; int len = Math.min(prevBuf.length, buf.length); boolean areEqual = true; for (int i = 0; i < len; i += 1) { int val1 = prevBuf[i] & 0xFF; int val2 = buf[i] & 0xFF; if (val1 < val2) { areEqual = false; break; } else if (val1 > val2) { errOffset = i; break; } } if (areEqual) { if (prevBuf.length < buf.length) { areEqual = false; } else if (prevBuf.length > buf.length) { areEqual = false; errOffset = buf.length + 1; } } if (errOffset != -1 || areEqual) { StringBuilder msg = new StringBuilder(); if (errOffset != -1) { msg.append("Left >= right at byte offset " + errOffset); } else if (areEqual) { msg.append("Bytes are equal"); } else { throw new IllegalStateException(); } msg.append("\nLeft hex bytes: "); for (int i = 0; i < prevBuf.length; i += 1) { msg.append(' '); int val = prevBuf[i] & 0xFF; if ((val & 0xF0) == 0) { msg.append('0'); } msg.append(Integer.toHexString(val)); } msg.append("\nRight hex bytes:"); for (int i = 0; i < buf.length; i += 1) { msg.append(' '); int val = buf[i] & 0xFF; if ((val & 0xF0) == 0) { msg.append('0'); } msg.append(Integer.toHexString(val)); } if (dataIndex >= 0) { msg.append("\nData index: " + dataIndex); } fail(msg.toString()); } } prevBuf = buf; out.reset(); } private void reset() { prevBuf = null; out.reset(); } @Test public void testString() { final String[] DATA = { "", "\u0001", "\u0002", "A", "a", "ab", "b", "bb", "bba", "c", "c\u0001", "d", new String(new char[] { 0x7F }), new String(new char[] { 0x7F, 0 }), new String(new char[] { 0xFF }), new String(new char[] { Character.MAX_VALUE }), }; for (int i = 0; i < DATA.length; i += 1) { out.writeString(DATA[i]); check(i); } reset(); out.writeString("a"); check(); out.writeString("a"); out.writeString(""); check(); out.writeString("a"); out.writeString(""); out.writeString("a"); check(); out.writeString("a"); out.writeString("b"); check(); out.writeString("aa"); check(); out.writeString("b"); check(); } @Test public void testFixedString() { final char[][] DATA = { {}, {'a'}, {'a', 'b'}, {'b'}, {'b', 'b'}, {0x7F}, {0xFF}, }; for (int i = 0; i < DATA.length; i += 1) { out.writeString(DATA[i]); check(i); } } @Test public void testChars() { final char[][] DATA = { {}, {0}, {'a'}, {'a', 0}, {'a', 'b'}, {'b'}, {'b', 'b'}, {0x7F}, {0x7F, 0}, {0xFF}, {0xFF, 0}, }; for (int i = 0; i < DATA.length; i += 1) { out.writeChars(DATA[i]); check(i); } } @Test public void testBytes() { final char[][] DATA = { {}, {0}, {'a'}, {'a', 0}, {'a', 'b'}, {'b'}, {'b', 'b'}, {0x7F}, {0xFF}, }; for (int i = 0; i < DATA.length; i += 1) { out.writeBytes(DATA[i]); check(i); } } @Test public void testBoolean() { final boolean[] DATA = { false, true }; for (int i = 0; i < DATA.length; i += 1) { out.writeBoolean(DATA[i]); check(i); } } @Test public void testUnsignedByte() { final int[] DATA = { 0, 1, 0x7F, 0xFF }; for (int i = 0; i < DATA.length; i += 1) { out.writeUnsignedByte(DATA[i]); check(i); } } @Test public void testUnsignedShort() { final int[] DATA = { 0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF }; for (int i = 0; i < DATA.length; i += 1) { out.writeUnsignedShort(DATA[i]); check(i); } } @Test public void testUnsignedInt() { final long[] DATA = { 0, 1, 0xFE, 0xFF, 0x800, 0x7FFF, 0xFFFF, 0x80000, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF }; for (int i = 0; i < DATA.length; i += 1) { out.writeUnsignedInt(DATA[i]); check(i); } } @Test public void testByte() { final byte[] DATA = { Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeByte(DATA[i]); check(i); } } @Test public void testShort() { final short[] DATA = { Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeShort(DATA[i]); check(i); } } @Test public void testInt() { final int[] DATA = { Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeInt(DATA[i]); check(i); } } @Test public void testLong() { final long[] DATA = { Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeLong(DATA[i]); check(i); } } @Test public void testFloat() { // Only positive floats and doubles are ordered deterministically final float[] DATA = { 0, Float.MIN_VALUE, 2 * Float.MIN_VALUE, (float) 0.01, (float) 0.02, (float) 0.99, 1, (float) 1.01, (float) 1.02, (float) 1.99, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE, Long.MAX_VALUE / 2, Long.MAX_VALUE, Float.MAX_VALUE, Float.POSITIVE_INFINITY, Float.NaN, }; for (int i = 0; i < DATA.length; i += 1) { out.writeFloat(DATA[i]); check(i); } } @Test public void testDouble() { // Only positive floats and doubles are ordered deterministically final double[] DATA = { 0, Double.MIN_VALUE, 2 * Double.MIN_VALUE, 0.001, 0.002, 0.999, 1, 1.001, 1.002, 1.999, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE / 2, Long.MAX_VALUE, Float.MAX_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN, }; for (int i = 0; i < DATA.length; i += 1) { out.writeDouble(DATA[i]); check(i); } } @Test public void testSortedFloat() { final float[] DATA = { Float.NEGATIVE_INFINITY, (- Float.MAX_VALUE), Long.MIN_VALUE, Long.MIN_VALUE / 2, Integer.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, (float) -1.99, (float) -1.02, (float) -1.01, -1, (float) -0.99, (float) -0.02, (float) -0.01, 2 * (- Float.MIN_VALUE), (- Float.MIN_VALUE), 0, Float.MIN_VALUE, 2 * Float.MIN_VALUE, (float) 0.01, (float) 0.02, (float) 0.99, 1, (float) 1.01, (float) 1.02, (float) 1.99, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE, Long.MAX_VALUE / 2, Long.MAX_VALUE, Float.MAX_VALUE, Float.POSITIVE_INFINITY, Float.NaN, }; for (int i = 0; i < DATA.length; i += 1) { out.writeSortedFloat(DATA[i]); check(i); } } @Test public void testSortedDouble() { final double[] DATA = { Double.NEGATIVE_INFINITY, (- Double.MAX_VALUE), (- Float.MAX_VALUE), Long.MIN_VALUE, Long.MIN_VALUE / 2, Integer.MIN_VALUE, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1.999, -1.002, -1.001, -1, -0.999, -0.002, -0.001, 2 * (- Double.MIN_VALUE), (- Double.MIN_VALUE), 0, Double.MIN_VALUE, 2 * Double.MIN_VALUE, 0.001, 0.002, 0.999, 1, 1.001, 1.002, 1.999, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE / 2, Long.MAX_VALUE, Float.MAX_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN, }; for (int i = 0; i < DATA.length; i += 1) { out.writeSortedDouble(DATA[i]); check(i); } } @Test public void testPackedIntAndLong() { /* Only packed int/long values from 0 to 630 are ordered correctly */ for (int i = 0; i <= 630; i += 1) { out.writePackedInt(i); check(i); } reset(); for (int i = 0; i <= 630; i += 1) { out.writePackedLong(i); check(i); } } @Test public void testSortedPackedInt() { final int[] DATA = { Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeSortedPackedInt(DATA[i]); check(i); } } @Test public void testSortedPackedLong() { final long[] DATA = { Long.MIN_VALUE, Long.MIN_VALUE + 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Short.MIN_VALUE, Short.MIN_VALUE + 1, Byte.MIN_VALUE, Byte.MIN_VALUE + 1, -1, 0, 1, Byte.MAX_VALUE - 1, Byte.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE, }; for (int i = 0; i < DATA.length; i += 1) { out.writeSortedPackedLong(DATA[i]); check(i); } } @Test public void testBigInteger() { final BigInteger[] DATA = { new BigInteger("-1111111111111111111111111"), new BigInteger("-11111111111111111111"), BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MIN_VALUE + 1), BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MIN_VALUE + 1), BigInteger.valueOf(Short.MIN_VALUE), BigInteger.valueOf(Short.MIN_VALUE + 1), BigInteger.valueOf(Byte.MIN_VALUE), BigInteger.valueOf(Byte.MIN_VALUE + 1), BigInteger.valueOf(-1), BigInteger.ZERO, BigInteger.ONE, BigInteger.valueOf(Byte.MAX_VALUE - 1), BigInteger.valueOf(Byte.MAX_VALUE), BigInteger.valueOf(Short.MAX_VALUE - 1), BigInteger.valueOf(Short.MAX_VALUE), BigInteger.valueOf(Integer.MAX_VALUE - 1), BigInteger.valueOf(Integer.MAX_VALUE), BigInteger.valueOf(Long.MAX_VALUE - 1), BigInteger.valueOf(Long.MAX_VALUE), new BigInteger("11111111111111111111"), new BigInteger("1111111111111111111111111"), }; for (int i = 0; i < DATA.length; i += 1) { out.writeBigInteger(DATA[i]); check(i); } } @Test public void testSortedBigDecimal() { final BigDecimal[] DATA = { new BigDecimal(BigInteger.valueOf(Long.MIN_VALUE), Short.MIN_VALUE), new BigDecimal("-9999999999999999999.9999999999999999999"), new BigDecimal("-123456789.123456789"), new BigDecimal("-0.9999999999999999999999999999999999999"), new BigDecimal("-123456789.123456789E-700"), new BigDecimal("-123456789.123456789E-6700"), new BigDecimal(BigInteger.valueOf(Long.MIN_VALUE), Short.MAX_VALUE), new BigDecimal("0.0"), new BigDecimal(BigInteger.valueOf(Long.MAX_VALUE), Short.MAX_VALUE), new BigDecimal("0.9999999999999999999999999999999999999"), new BigDecimal("123456789.123456789"), new BigDecimal("9999999999999999999.9999999999999999999"), new BigDecimal("123456789.123456789E700"), new BigDecimal("123456789.123456789E6700"), new BigDecimal(BigInteger.valueOf(Long.MAX_VALUE), Short.MIN_VALUE), }; for (int i = 0; i < DATA.length; i += 1) { out.writeSortedBigDecimal(DATA[i]); check(i); } } }