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