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