1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.hash;
18
19 import static com.google.common.io.BaseEncoding.base16;
20
21 import com.google.common.base.Charsets;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.io.BaseEncoding;
24 import com.google.common.testing.ClassSanityTester;
25
26 import junit.framework.TestCase;
27
28 import java.util.Arrays;
29
30
31
32
33
34
35
36 public class HashCodeTest extends TestCase {
37
38 private static final ImmutableList<ExpectedHashCode> expectedHashCodes = ImmutableList.of(
39 new ExpectedHashCode(new byte[] {
40 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89,
41 (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01},
42 0x89abcdef, 0x0123456789abcdefL, "efcdab8967452301"),
43
44 new ExpectedHashCode(new byte[] {
45 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89,
46 (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01,
47 (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
48 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08},
49 0x89abcdef, 0x0123456789abcdefL,
50 "efcdab89674523010102030405060708"),
51
52 new ExpectedHashCode(new byte[] { (byte) 0xdf, (byte) 0x9b, (byte) 0x57, (byte) 0x13 },
53 0x13579bdf, null, "df9b5713"),
54
55 new ExpectedHashCode(new byte[] {
56 (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00},
57 0x0000abcd, null, "cdab0000"),
58
59 new ExpectedHashCode(new byte[] {
60 (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x00,
61 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00},
62 0x00abcdef, 0x0000000000abcdefL, "efcdab0000000000")
63 );
64
65
66 public void testFromInt() {
67 for (ExpectedHashCode expected : expectedHashCodes) {
68 if (expected.bytes.length == 4) {
69 HashCode fromInt = HashCode.fromInt(expected.asInt);
70 assertExpectedHashCode(expected, fromInt);
71 }
72 }
73 }
74
75
76 public void testFromLong() {
77 for (ExpectedHashCode expected : expectedHashCodes) {
78 if (expected.bytes.length == 8) {
79 HashCode fromLong = HashCode.fromLong(expected.asLong);
80 assertExpectedHashCode(expected, fromLong);
81 }
82 }
83 }
84
85 public void testFromBytes() {
86 for (ExpectedHashCode expected : expectedHashCodes) {
87 HashCode fromBytes = HashCode.fromBytes(expected.bytes);
88 assertExpectedHashCode(expected, fromBytes);
89 }
90 }
91
92 public void testFromBytes_copyOccurs() {
93 byte[] bytes = new byte[] { (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00 };
94 HashCode hashCode = HashCode.fromBytes(bytes);
95 int expectedInt = 0x0000abcd;
96 String expectedToString = "cdab0000";
97
98 assertEquals(expectedInt, hashCode.asInt());
99 assertEquals(expectedToString, hashCode.toString());
100
101 bytes[0] = (byte) 0x00;
102
103 assertEquals(expectedInt, hashCode.asInt());
104 assertEquals(expectedToString, hashCode.toString());
105 }
106
107 public void testFromBytesNoCopy_noCopyOccurs() {
108 byte[] bytes = new byte[] { (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00 };
109 HashCode hashCode = HashCode.fromBytesNoCopy(bytes);
110
111 assertEquals(0x0000abcd, hashCode.asInt());
112 assertEquals("cdab0000", hashCode.toString());
113
114 bytes[0] = (byte) 0x00;
115
116 assertEquals(0x0000ab00, hashCode.asInt());
117 assertEquals("00ab0000", hashCode.toString());
118 }
119
120 public void testGetBytesInternal_noCloneOccurs() {
121 byte[] bytes = new byte[] { (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00 };
122 HashCode hashCode = HashCode.fromBytes(bytes);
123
124 assertEquals(0x0000abcd, hashCode.asInt());
125 assertEquals("cdab0000", hashCode.toString());
126
127 hashCode.getBytesInternal()[0] = (byte) 0x00;
128
129 assertEquals(0x0000ab00, hashCode.asInt());
130 assertEquals("00ab0000", hashCode.toString());
131 }
132
133 public void testPadToLong() {
134 assertEquals(0x1111111111111111L, HashCode.fromLong(0x1111111111111111L).padToLong());
135 assertEquals(0x9999999999999999L, HashCode.fromLong(0x9999999999999999L).padToLong());
136 assertEquals(0x0000000011111111L, HashCode.fromInt(0x11111111).padToLong());
137 assertEquals(0x0000000099999999L, HashCode.fromInt(0x99999999).padToLong());
138 }
139
140 public void testPadToLongWith4Bytes() {
141 assertEquals(0x0000000099999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(4)).padToLong());
142 }
143
144 public void testPadToLongWith6Bytes() {
145 assertEquals(0x0000999999999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(6)).padToLong());
146 }
147
148 public void testPadToLongWith8Bytes() {
149 assertEquals(0x9999999999999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(8)).padToLong());
150 }
151
152 private static byte[] byteArrayWith9s(int size) {
153 byte[] bytez = new byte[size];
154 Arrays.fill(bytez, (byte) 0x99);
155 return bytez;
156 }
157
158 public void testToString() {
159 byte[] data = new byte[] { 127, -128, 5, -1, 14 };
160 assertEquals("7f8005ff0e", HashCode.fromBytes(data).toString());
161 assertEquals("7f8005ff0e", base16().lowerCase().encode(data));
162 }
163
164 public void testHashCode_nulls() throws Exception {
165 sanityTester().testNulls();
166 }
167
168 public void testHashCode_equalsAndSerializable() throws Exception {
169 sanityTester().testEqualsAndSerializable();
170 }
171
172 public void testRoundTripHashCodeUsingBaseEncoding() {
173 HashCode hash1 = Hashing.sha1().hashString("foo", Charsets.US_ASCII);
174 HashCode hash2 =
175 HashCode.fromBytes(BaseEncoding.base16().lowerCase().decode(hash1.toString()));
176 assertEquals(hash1, hash2);
177 }
178
179 public void testObjectHashCode() {
180 HashCode hashCode42 = HashCode.fromInt(42);
181 assertEquals(42, hashCode42.hashCode());
182 }
183
184
185 public void testObjectHashCodeWithSameLowOrderBytes() {
186
187 byte[] bytesA = new byte[5];
188 byte[] bytesB = new byte[5];
189
190
191 bytesA[4] = (byte) 0xbe;
192 bytesB[4] = (byte) 0xef;
193
194 HashCode hashCodeA = HashCode.fromBytes(bytesA);
195 HashCode hashCodeB = HashCode.fromBytes(bytesB);
196
197
198 assertFalse(hashCodeA.equals(hashCodeB));
199
200
201
202 assertEquals(hashCodeA.hashCode(), hashCodeB.hashCode());
203 }
204
205 public void testRoundTripHashCodeUsingFromString() {
206 HashCode hash1 = Hashing.sha1().hashString("foo", Charsets.US_ASCII);
207 HashCode hash2 = HashCode.fromString(hash1.toString());
208 assertEquals(hash1, hash2);
209 }
210
211 public void testRoundTrip() {
212 for (ExpectedHashCode expected : expectedHashCodes) {
213 String string = HashCode.fromBytes(expected.bytes).toString();
214 assertEquals(expected.toString, string);
215 assertEquals(
216 expected.toString,
217 HashCode.fromBytes(
218 BaseEncoding.base16().lowerCase().decode(string)).toString());
219 }
220 }
221
222 public void testFromStringFailsWithInvalidHexChar() {
223 try {
224 HashCode.fromString("7f8005ff0z");
225 fail();
226 } catch (IllegalArgumentException expected) {
227 }
228 }
229
230 public void testFromStringFailsWithUpperCaseString() {
231 String string = Hashing.sha1().hashString("foo", Charsets.US_ASCII).toString().toUpperCase();
232 try {
233 HashCode.fromString(string);
234 fail();
235 } catch (IllegalArgumentException expected) {
236 }
237 }
238
239 public void testFromStringFailsWithShortInputs() {
240 try {
241 HashCode.fromString("");
242 fail();
243 } catch (IllegalArgumentException expected) {
244 }
245 try {
246 HashCode.fromString("7");
247 fail();
248 } catch (IllegalArgumentException expected) {
249 }
250 HashCode.fromString("7f");
251 }
252
253 public void testFromStringFailsWithOddLengthInput() {
254 try {
255 HashCode.fromString("7f8");
256 fail();
257 } catch (IllegalArgumentException expected) {
258 }
259 }
260
261 public void testIntWriteBytesTo() {
262 byte[] dest = new byte[4];
263 HashCode.fromInt(42).writeBytesTo(dest, 0, 4);
264 assertTrue(Arrays.equals(
265 HashCode.fromInt(42).asBytes(),
266 dest));
267 }
268
269 public void testLongWriteBytesTo() {
270 byte[] dest = new byte[8];
271 HashCode.fromLong(42).writeBytesTo(dest, 0, 8);
272 assertTrue(Arrays.equals(
273 HashCode.fromLong(42).asBytes(),
274 dest));
275 }
276
277 private static final HashCode HASH_ABCD =
278 HashCode.fromBytes(new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd });
279
280 public void testWriteBytesTo() {
281 byte[] dest = new byte[4];
282 HASH_ABCD.writeBytesTo(dest, 0, 4);
283 assertTrue(Arrays.equals(
284 new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd },
285 dest));
286 }
287
288 public void testWriteBytesToOversizedArray() {
289 byte[] dest = new byte[5];
290 HASH_ABCD.writeBytesTo(dest, 0, 4);
291 assertTrue(Arrays.equals(
292 new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 0x00 },
293 dest));
294 }
295
296 public void testWriteBytesToOversizedArrayLongMaxLength() {
297 byte[] dest = new byte[5];
298 HASH_ABCD.writeBytesTo(dest, 0, 5);
299 assertTrue(Arrays.equals(
300 new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 0x00 },
301 dest));
302 }
303
304 public void testWriteBytesToOversizedArrayShortMaxLength() {
305 byte[] dest = new byte[5];
306 HASH_ABCD.writeBytesTo(dest, 0, 3);
307 assertTrue(Arrays.equals(
308 new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0x00, (byte) 0x00 },
309 dest));
310 }
311
312 public void testWriteBytesToUndersizedArray() {
313 byte[] dest = new byte[3];
314 try {
315 HASH_ABCD.writeBytesTo(dest, 0, 4);
316 fail();
317 } catch (IndexOutOfBoundsException expected) {
318 }
319 }
320
321 public void testWriteBytesToUndersizedArrayLongMaxLength() {
322 byte[] dest = new byte[3];
323 try {
324 HASH_ABCD.writeBytesTo(dest, 0, 5);
325 fail();
326 } catch (IndexOutOfBoundsException expected) {
327 }
328 }
329
330 public void testWriteBytesToUndersizedArrayShortMaxLength() {
331 byte[] dest = new byte[3];
332 HASH_ABCD.writeBytesTo(dest, 0, 2);
333 assertTrue(Arrays.equals(
334 new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0x00 },
335 dest));
336 }
337
338 private static ClassSanityTester.FactoryMethodReturnValueTester sanityTester() {
339 return new ClassSanityTester()
340 .setDefault(byte[].class, new byte[] {1, 2, 3, 4})
341 .setDistinctValues(byte[].class, new byte[] {1, 2, 3, 4}, new byte[] {5, 6, 7, 8})
342 .setDistinctValues(String.class, "7f8005ff0e", "7f8005ff0f")
343 .forAllPublicStaticMethods(HashCode.class);
344 }
345
346 private static void assertExpectedHashCode(ExpectedHashCode expectedHashCode, HashCode hash) {
347 assertTrue(Arrays.equals(expectedHashCode.bytes, hash.asBytes()));
348 byte[] bb = new byte[hash.bits() / 8];
349 hash.writeBytesTo(bb, 0, bb.length);
350 assertTrue(Arrays.equals(expectedHashCode.bytes, bb));
351 assertEquals(expectedHashCode.asInt, hash.asInt());
352 if (expectedHashCode.asLong == null) {
353 try {
354 hash.asLong();
355 fail();
356 } catch (IllegalStateException expected) {}
357 } else {
358 assertEquals(expectedHashCode.asLong.longValue(), hash.asLong());
359 }
360 assertEquals(expectedHashCode.toString, hash.toString());
361 assertSideEffectFree(hash);
362 assertReadableBytes(hash);
363 }
364
365 private static void assertSideEffectFree(HashCode hash) {
366 byte[] original = hash.asBytes();
367 byte[] mutated = hash.asBytes();
368 mutated[0]++;
369 assertTrue(Arrays.equals(original, hash.asBytes()));
370 }
371
372 private static void assertReadableBytes(HashCode hashCode) {
373 assertTrue(hashCode.bits() >= 32);
374 byte[] hashBytes = hashCode.asBytes();
375 int totalBytes = hashCode.bits() / 8;
376
377 for (int bytes = 0; bytes < totalBytes; bytes++) {
378 byte[] bb = new byte[bytes];
379 hashCode.writeBytesTo(bb, 0, bb.length);
380
381 assertTrue(Arrays.equals(Arrays.copyOf(hashBytes, bytes), bb));
382 }
383 }
384
385 private static class ExpectedHashCode {
386 final byte[] bytes;
387 final int asInt;
388 final Long asLong;
389 final String toString;
390 ExpectedHashCode(byte[] bytes, int asInt, Long asLong, String toString) {
391 this.bytes = bytes;
392 this.asInt = asInt;
393 this.asLong = asLong;
394 this.toString = toString;
395 }
396 }
397 }