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