• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
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  * Unit tests for {@link HashCode}.
32  *
33  * @author Dimitris Andreou
34  * @author Kurt Alfred Kluever
35  */
36 public class HashCodeTest extends TestCase {
37   // note: asInt(), asLong() are in little endian
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, // up to here, same bytes as above
47         (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
48         (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08},
49         0x89abcdef, 0x0123456789abcdefL, // asInt/asLong as above, due to equal eight first bytes
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   // expectedHashCodes must contain at least one hash code with 4 bytes
testFromInt()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   // expectedHashCodes must contain at least one hash code with 8 bytes
testFromLong()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 
testFromBytes()85   public void testFromBytes() {
86     for (ExpectedHashCode expected : expectedHashCodes) {
87       HashCode fromBytes = HashCode.fromBytes(expected.bytes);
88       assertExpectedHashCode(expected, fromBytes);
89     }
90   }
91 
testFromBytes_copyOccurs()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 
testFromBytesNoCopy_noCopyOccurs()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 
testGetBytesInternal_noCloneOccurs()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 
testPadToLong()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 
testPadToLongWith4Bytes()140   public void testPadToLongWith4Bytes() {
141     assertEquals(0x0000000099999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(4)).padToLong());
142   }
143 
testPadToLongWith6Bytes()144   public void testPadToLongWith6Bytes() {
145     assertEquals(0x0000999999999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(6)).padToLong());
146   }
147 
testPadToLongWith8Bytes()148   public void testPadToLongWith8Bytes() {
149     assertEquals(0x9999999999999999L, HashCode.fromBytesNoCopy(byteArrayWith9s(8)).padToLong());
150   }
151 
byteArrayWith9s(int size)152   private static byte[] byteArrayWith9s(int size) {
153     byte[] bytez = new byte[size];
154     Arrays.fill(bytez, (byte) 0x99);
155     return bytez;
156   }
157 
testToString()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 
testHashCode_nulls()164   public void testHashCode_nulls() throws Exception {
165     sanityTester().testNulls();
166   }
167 
testHashCode_equalsAndSerializable()168   public void testHashCode_equalsAndSerializable() throws Exception {
169     sanityTester().testEqualsAndSerializable();
170   }
171 
testRoundTripHashCodeUsingBaseEncoding()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 
testObjectHashCode()179   public void testObjectHashCode() {
180     HashCode hashCode42 = HashCode.fromInt(42);
181     assertEquals(42, hashCode42.hashCode());
182   }
183 
184   // See https://code.google.com/p/guava-libraries/issues/detail?id=1494
testObjectHashCodeWithSameLowOrderBytes()185   public void testObjectHashCodeWithSameLowOrderBytes() {
186     // These will have the same first 4 bytes (all 0).
187     byte[] bytesA = new byte[5];
188     byte[] bytesB = new byte[5];
189 
190     // Change only the last (5th) byte
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     // They aren't equal...
198     assertFalse(hashCodeA.equals(hashCodeB));
199 
200     // But they still have the same Object#hashCode() value.
201     // Technically not a violation of the equals/hashCode contract, but...?
202     assertEquals(hashCodeA.hashCode(), hashCodeB.hashCode());
203   }
204 
testRoundTripHashCodeUsingFromString()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 
testRoundTrip()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 
testFromStringFailsWithInvalidHexChar()222   public void testFromStringFailsWithInvalidHexChar() {
223     try {
224       HashCode.fromString("7f8005ff0z");
225       fail();
226     } catch (IllegalArgumentException expected) {
227     }
228   }
229 
testFromStringFailsWithUpperCaseString()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 
testFromStringFailsWithShortInputs()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 
testFromStringFailsWithOddLengthInput()253   public void testFromStringFailsWithOddLengthInput() {
254     try {
255       HashCode.fromString("7f8");
256       fail();
257     } catch (IllegalArgumentException expected) {
258     }
259   }
260 
testIntWriteBytesTo()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 
testLongWriteBytesTo()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 
testWriteBytesTo()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 
testWriteBytesToOversizedArray()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 
testWriteBytesToOversizedArrayLongMaxLength()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 
testWriteBytesToOversizedArrayShortMaxLength()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 
testWriteBytesToUndersizedArray()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 
testWriteBytesToUndersizedArrayLongMaxLength()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 
testWriteBytesToUndersizedArrayShortMaxLength()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 
sanityTester()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 
assertExpectedHashCode(ExpectedHashCode expectedHashCode, HashCode hash)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 
assertSideEffectFree(HashCode hash)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 
assertReadableBytes(HashCode hashCode)372   private static void assertReadableBytes(HashCode hashCode) {
373     assertTrue(hashCode.bits() >= 32); // sanity
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; // null means that asLong should throw an exception
389     final String toString;
ExpectedHashCode(byte[] bytes, int asInt, Long asLong, 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 }
398