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