// Copyright 2011 Google Inc. All Rights Reserved. package com.google.common.hash; import com.google.common.collect.ImmutableList; import junit.framework.TestCase; import java.util.Arrays; /** * Tests for HashCodes, especially making sure that their endianness promises (big-endian) * are upheld. * * @author andreou@google.com (Dimitris Andreou) */ public class HashCodesTest extends TestCase { // note: asInt(), asLong() are in little endian private static final ImmutableList<ExpectedHashCode> expectedHashCodes = ImmutableList.of( new ExpectedHashCode(new byte[] { (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01}, 0x89abcdef, 0x0123456789abcdefL, "efcdab8967452301"), new ExpectedHashCode(new byte[] { (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01, // up to here, same bytes as above (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08}, 0x89abcdef, 0x0123456789abcdefL, // asInt/asLong as above, due to equal eight first bytes "efcdab89674523010102030405060708"), new ExpectedHashCode(new byte[] { (byte) 0xdf, (byte) 0x9b, (byte) 0x57, (byte) 0x13 }, 0x13579bdf, null, "df9b5713"), new ExpectedHashCode(new byte[] { (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00}, 0x0000abcd, null, "cdab0000"), new ExpectedHashCode(new byte[] { (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, 0x00abcdef, 0x0000000000abcdefL, "efcdab0000000000") ); // expectedHashCodes must contain at least one hash code with 4 bytes public void testFromInt() { for (ExpectedHashCode expected : expectedHashCodes) { if (expected.bytes.length == 4) { HashCode fromInt = HashCodes.fromInt(expected.asInt); assertExpectedHashCode(expected, fromInt); } } } // expectedHashCodes must contain at least one hash code with 8 bytes public void testFromLong() { for (ExpectedHashCode expected : expectedHashCodes) { if (expected.bytes.length == 8) { HashCode fromLong = HashCodes.fromLong(expected.asLong); assertExpectedHashCode(expected, fromLong); } } } public void testFromBytes() { for (ExpectedHashCode expected : expectedHashCodes) { HashCode fromBytes = HashCodes.fromBytes(expected.bytes); assertExpectedHashCode(expected, fromBytes); } } private void assertExpectedHashCode(ExpectedHashCode expected, HashCode hash) { assertTrue(Arrays.equals(expected.bytes, hash.asBytes())); byte[] bb = new byte[hash.bits() / 8]; hash.writeBytesTo(bb, 0, bb.length); assertTrue(Arrays.equals(expected.bytes, bb)); assertEquals(expected.asInt, hash.asInt()); if (expected.asLong == null) { try { hash.asLong(); fail(); } catch (IllegalStateException ok) {} } else { assertEquals(expected.asLong.longValue(), hash.asLong()); } assertEquals(expected.toString, hash.toString()); assertSideEffectFree(hash); assertReadableBytes(hash); } private void assertSideEffectFree(HashCode hash) { byte[] original = hash.asBytes(); byte[] mutated = hash.asBytes(); mutated[0]++; assertTrue(Arrays.equals(original, hash.asBytes())); } private void assertReadableBytes(HashCode hashCode) { assertTrue(hashCode.bits() >= 32); // sanity byte[] hashBytes = hashCode.asBytes(); int totalBytes = hashCode.bits() / 8; for (int bytes = 0; bytes < totalBytes; bytes++) { byte[] bb = new byte[bytes]; hashCode.writeBytesTo(bb, 0, bb.length); assertTrue(Arrays.equals(Arrays.copyOf(hashBytes, bytes), bb)); } } private static class ExpectedHashCode { final byte[] bytes; final int asInt; final Long asLong; // null means that asLong should throw an exception final String toString; ExpectedHashCode(byte[] bytes, int asInt, Long asLong, String toString) { this.bytes = bytes; this.asInt = asInt; this.asLong = asLong; this.toString = toString; } } }