1 // Copyright 2011 Google Inc. All Rights Reserved. 2 3 package com.google.common.hash; 4 5 import com.google.common.collect.ImmutableList; 6 7 import junit.framework.TestCase; 8 9 import java.util.Arrays; 10 11 /** 12 * Tests for HashCodes, especially making sure that their endianness promises (big-endian) 13 * are upheld. 14 * 15 * @author andreou (at) google.com (Dimitris Andreou) 16 */ 17 public class HashCodesTest extends TestCase { 18 // note: asInt(), asLong() are in little endian 19 private static final ImmutableList<ExpectedHashCode> expectedHashCodes = ImmutableList.of( 20 new ExpectedHashCode(new byte[] { 21 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89, 22 (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01}, 23 0x89abcdef, 0x0123456789abcdefL, "efcdab8967452301"), 24 25 new ExpectedHashCode(new byte[] { 26 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89, 27 (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01, // up to here, same bytes as above 28 (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, 29 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08}, 30 0x89abcdef, 0x0123456789abcdefL, // asInt/asLong as above, due to equal eight first bytes 31 "efcdab89674523010102030405060708"), 32 33 new ExpectedHashCode(new byte[] { (byte) 0xdf, (byte) 0x9b, (byte) 0x57, (byte) 0x13 }, 34 0x13579bdf, null, "df9b5713"), 35 36 new ExpectedHashCode(new byte[] { 37 (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00}, 38 0x0000abcd, null, "cdab0000"), 39 40 new ExpectedHashCode(new byte[] { 41 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x00, 42 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, 43 0x00abcdef, 0x0000000000abcdefL, "efcdab0000000000") 44 ); 45 46 // expectedHashCodes must contain at least one hash code with 4 bytes 47 public void testFromInt() { 48 for (ExpectedHashCode expected : expectedHashCodes) { 49 if (expected.bytes.length == 4) { 50 HashCode fromInt = HashCodes.fromInt(expected.asInt); 51 assertExpectedHashCode(expected, fromInt); 52 } 53 } 54 } 55 56 // expectedHashCodes must contain at least one hash code with 8 bytes 57 public void testFromLong() { 58 for (ExpectedHashCode expected : expectedHashCodes) { 59 if (expected.bytes.length == 8) { 60 HashCode fromLong = HashCodes.fromLong(expected.asLong); 61 assertExpectedHashCode(expected, fromLong); 62 } 63 } 64 } 65 66 public void testFromBytes() { 67 for (ExpectedHashCode expected : expectedHashCodes) { 68 HashCode fromBytes = HashCodes.fromBytes(expected.bytes); 69 assertExpectedHashCode(expected, fromBytes); 70 } 71 } 72 73 private void assertExpectedHashCode(ExpectedHashCode expected, HashCode hash) { 74 assertTrue(Arrays.equals(expected.bytes, hash.asBytes())); 75 byte[] bb = new byte[hash.bits() / 8]; 76 hash.writeBytesTo(bb, 0, bb.length); 77 assertTrue(Arrays.equals(expected.bytes, bb)); 78 assertEquals(expected.asInt, hash.asInt()); 79 if (expected.asLong == null) { 80 try { 81 hash.asLong(); 82 fail(); 83 } catch (IllegalStateException ok) {} 84 } else { 85 assertEquals(expected.asLong.longValue(), hash.asLong()); 86 } 87 assertEquals(expected.toString, hash.toString()); 88 assertSideEffectFree(hash); 89 assertReadableBytes(hash); 90 } 91 92 private void assertSideEffectFree(HashCode hash) { 93 byte[] original = hash.asBytes(); 94 byte[] mutated = hash.asBytes(); 95 mutated[0]++; 96 assertTrue(Arrays.equals(original, hash.asBytes())); 97 } 98 99 private void assertReadableBytes(HashCode hashCode) { 100 assertTrue(hashCode.bits() >= 32); // sanity 101 byte[] hashBytes = hashCode.asBytes(); 102 int totalBytes = hashCode.bits() / 8; 103 104 for (int bytes = 0; bytes < totalBytes; bytes++) { 105 byte[] bb = new byte[bytes]; 106 hashCode.writeBytesTo(bb, 0, bb.length); 107 108 assertTrue(Arrays.equals(Arrays.copyOf(hashBytes, bytes), bb)); 109 } 110 } 111 112 private static class ExpectedHashCode { 113 final byte[] bytes; 114 final int asInt; 115 final Long asLong; // null means that asLong should throw an exception 116 final String toString; 117 ExpectedHashCode(byte[] bytes, int asInt, Long asLong, String toString) { 118 this.bytes = bytes; 119 this.asInt = asInt; 120 this.asLong = asLong; 121 this.toString = toString; 122 } 123 } 124 } 125