1// Copyright 2011 Google Inc. All Rights Reserved.
2
3package com.google.common.hash;
4
5import com.google.common.collect.ImmutableList;
6
7import junit.framework.TestCase;
8
9import 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@google.com (Dimitris Andreou)
16 */
17public 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