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.hash.Hashing.ConcatenatedHashFunction; 20 import static com.google.common.hash.Hashing.goodFastHash; 21 22 import com.google.common.base.Charsets; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.ImmutableTable; 25 import com.google.common.collect.Lists; 26 import com.google.common.collect.Table.Cell; 27 import com.google.common.primitives.Ints; 28 import com.google.common.testing.EqualsTester; 29 import com.google.common.testing.NullPointerTester; 30 import com.google.common.util.concurrent.AtomicLongMap; 31 32 import junit.framework.TestCase; 33 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Modifier; 36 import java.nio.ByteBuffer; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.Random; 40 41 /** 42 * Unit tests for {@link Hashing}. 43 * 44 * @author Dimitris Andreou 45 * @author Kurt Alfred Kluever 46 */ 47 public class HashingTest extends TestCase { 48 public void testMd5() { 49 HashTestUtils.checkAvalanche(Hashing.md5(), 100, 0.4); 50 HashTestUtils.checkNo2BitCharacteristics(Hashing.md5()); 51 HashTestUtils.checkNoFunnels(Hashing.md5()); 52 HashTestUtils.assertInvariants(Hashing.md5()); 53 assertEquals("Hashing.md5()", Hashing.md5().toString()); 54 } 55 56 public void testSha1() { 57 HashTestUtils.checkAvalanche(Hashing.sha1(), 100, 0.4); 58 HashTestUtils.checkNo2BitCharacteristics(Hashing.sha1()); 59 HashTestUtils.checkNoFunnels(Hashing.sha1()); 60 HashTestUtils.assertInvariants(Hashing.sha1()); 61 assertEquals("Hashing.sha1()", Hashing.sha1().toString()); 62 } 63 64 public void testSha256() { 65 HashTestUtils.checkAvalanche(Hashing.sha256(), 100, 0.4); 66 HashTestUtils.checkNo2BitCharacteristics(Hashing.sha256()); 67 HashTestUtils.checkNoFunnels(Hashing.sha256()); 68 HashTestUtils.assertInvariants(Hashing.sha256()); 69 assertEquals("Hashing.sha256()", Hashing.sha256().toString()); 70 } 71 72 public void testSha512() { 73 HashTestUtils.checkAvalanche(Hashing.sha512(), 100, 0.4); 74 HashTestUtils.checkNo2BitCharacteristics(Hashing.sha512()); 75 HashTestUtils.checkNoFunnels(Hashing.sha512()); 76 HashTestUtils.assertInvariants(Hashing.sha512()); 77 assertEquals("Hashing.sha512()", Hashing.sha512().toString()); 78 } 79 80 public void testCrc32() { 81 HashTestUtils.assertInvariants(Hashing.crc32()); 82 assertEquals("Hashing.crc32()", Hashing.crc32().toString()); 83 } 84 85 public void testAdler32() { 86 HashTestUtils.assertInvariants(Hashing.adler32()); 87 assertEquals("Hashing.adler32()", Hashing.adler32().toString()); 88 } 89 90 public void testMurmur3_128() { 91 HashTestUtils.check2BitAvalanche(Hashing.murmur3_128(), 250, 0.20); 92 HashTestUtils.checkAvalanche(Hashing.murmur3_128(), 250, 0.17); 93 HashTestUtils.checkNo2BitCharacteristics(Hashing.murmur3_128()); 94 HashTestUtils.checkNoFunnels(Hashing.murmur3_128()); 95 HashTestUtils.assertInvariants(Hashing.murmur3_128()); 96 assertEquals("Hashing.murmur3_128(0)", Hashing.murmur3_128().toString()); 97 } 98 99 public void testMurmur3_32() { 100 HashTestUtils.check2BitAvalanche(Hashing.murmur3_32(), 250, 0.20); 101 HashTestUtils.checkAvalanche(Hashing.murmur3_32(), 250, 0.17); 102 HashTestUtils.checkNo2BitCharacteristics(Hashing.murmur3_32()); 103 HashTestUtils.checkNoFunnels(Hashing.murmur3_32()); 104 HashTestUtils.assertInvariants(Hashing.murmur3_32()); 105 assertEquals("Hashing.murmur3_32(0)", Hashing.murmur3_32().toString()); 106 } 107 108 public void testSipHash24() { 109 HashTestUtils.check2BitAvalanche(Hashing.sipHash24(), 250, 0.14); 110 HashTestUtils.checkAvalanche(Hashing.sipHash24(), 250, 0.10); 111 HashTestUtils.checkNo2BitCharacteristics(Hashing.sipHash24()); 112 HashTestUtils.checkNoFunnels(Hashing.sipHash24()); 113 HashTestUtils.assertInvariants(Hashing.sipHash24()); 114 assertEquals("Hashing.sipHash24(506097522914230528, 1084818905618843912)", 115 Hashing.sipHash24().toString()); 116 } 117 118 public void testGoodFastHash() { 119 for (int i = 1; i < 200; i += 17) { 120 HashFunction hasher = Hashing.goodFastHash(i); 121 assertTrue(hasher.bits() >= i); 122 HashTestUtils.assertInvariants(hasher); 123 } 124 } 125 126 // goodFastHash(32) uses Murmur3_32. Use the same epsilon bounds. 127 public void testGoodFastHash32() { 128 HashTestUtils.check2BitAvalanche(Hashing.goodFastHash(32), 250, 0.20); 129 HashTestUtils.checkAvalanche(Hashing.goodFastHash(32), 250, 0.17); 130 HashTestUtils.checkNo2BitCharacteristics(Hashing.goodFastHash(32)); 131 HashTestUtils.checkNoFunnels(Hashing.goodFastHash(32)); 132 HashTestUtils.assertInvariants(Hashing.goodFastHash(32)); 133 } 134 135 // goodFastHash(128) uses Murmur3_128. Use the same epsilon bounds. 136 public void testGoodFastHash128() { 137 HashTestUtils.check2BitAvalanche(Hashing.goodFastHash(128), 250, 0.20); 138 HashTestUtils.checkAvalanche(Hashing.goodFastHash(128), 250, 0.17); 139 HashTestUtils.checkNo2BitCharacteristics(Hashing.goodFastHash(128)); 140 HashTestUtils.checkNoFunnels(Hashing.goodFastHash(128)); 141 HashTestUtils.assertInvariants(Hashing.goodFastHash(128)); 142 } 143 144 // goodFastHash(256) uses Murmur3_128. Use the same epsilon bounds. 145 public void testGoodFastHash256() { 146 HashTestUtils.check2BitAvalanche(Hashing.goodFastHash(256), 250, 0.20); 147 HashTestUtils.checkAvalanche(Hashing.goodFastHash(256), 250, 0.17); 148 HashTestUtils.checkNo2BitCharacteristics(Hashing.goodFastHash(256)); 149 HashTestUtils.checkNoFunnels(Hashing.goodFastHash(256)); 150 HashTestUtils.assertInvariants(Hashing.goodFastHash(256)); 151 } 152 153 public void testConsistentHash_correctness() { 154 long[] interestingValues = { -1, 0, 1, 2, Long.MAX_VALUE, Long.MIN_VALUE }; 155 for (long h : interestingValues) { 156 checkConsistentHashCorrectness(h); 157 } 158 Random r = new Random(7); 159 for (int i = 0; i < 20; i++) { 160 checkConsistentHashCorrectness(r.nextLong()); 161 } 162 } 163 164 private void checkConsistentHashCorrectness(long hashCode) { 165 int last = 0; 166 for (int shards = 1; shards <= 100000; shards++) { 167 int b = Hashing.consistentHash(hashCode, shards); 168 if (b != last) { 169 assertEquals(shards - 1, b); 170 last = b; 171 } 172 } 173 } 174 175 public void testConsistentHash_probabilities() { 176 AtomicLongMap<Integer> map = AtomicLongMap.create(); 177 Random r = new Random(9); 178 for (int i = 0; i < ITERS; i++) { 179 countRemaps(r.nextLong(), map); 180 } 181 for (int shard = 2; shard <= MAX_SHARDS; shard++) { 182 // Rough: don't exceed 1.2x the expected number of remaps by more than 20 183 assertTrue(map.get(shard) <= 1.2 * ITERS / shard + 20); 184 } 185 } 186 187 private void countRemaps(long h, AtomicLongMap<Integer> map) { 188 int last = 0; 189 for (int shards = 2; shards <= MAX_SHARDS; shards++) { 190 int chosen = Hashing.consistentHash(h, shards); 191 if (chosen != last) { 192 map.incrementAndGet(shards); 193 last = chosen; 194 } 195 } 196 } 197 198 private static final int ITERS = 10000; 199 private static final int MAX_SHARDS = 500; 200 201 public void testConsistentHash_outOfRange() { 202 try { 203 Hashing.consistentHash(5L, 0); 204 fail(); 205 } catch (IllegalArgumentException expected) { 206 } 207 } 208 209 public void testConsistentHash_ofHashCode() { 210 checkSameResult(HashCode.fromLong(1), 1); 211 checkSameResult(HashCode.fromLong(0x9999999999999999L), 0x9999999999999999L); 212 checkSameResult(HashCode.fromInt(0x99999999), 0x0000000099999999L); 213 } 214 215 public void checkSameResult(HashCode hashCode, long equivLong) { 216 assertEquals(Hashing.consistentHash(equivLong, 5555), Hashing.consistentHash(hashCode, 5555)); 217 } 218 219 /** 220 * Check a few "golden" values to see that implementations across languages 221 * are equivalent. 222 */ 223 public void testConsistentHash_linearCongruentialGeneratorCompatibility() { 224 int[] golden100 = 225 { 0, 55, 62, 8, 45, 59, 86, 97, 82, 59, 226 73, 37, 17, 56, 86, 21, 90, 37, 38, 83 }; 227 for (int i = 0; i < golden100.length; i++) { 228 assertEquals(golden100[i], Hashing.consistentHash(i, 100)); 229 } 230 assertEquals(6, Hashing.consistentHash(10863919174838991L, 11)); 231 assertEquals(3, Hashing.consistentHash(2016238256797177309L, 11)); 232 assertEquals(5, Hashing.consistentHash(1673758223894951030L, 11)); 233 assertEquals(80343, Hashing.consistentHash(2, 100001)); 234 assertEquals(22152, Hashing.consistentHash(2201, 100001)); 235 assertEquals(15018, Hashing.consistentHash(2202, 100001)); 236 } 237 238 private static final double MAX_PERCENT_SPREAD = 0.5; 239 private static final long RANDOM_SEED = 177L; 240 241 public void testCombineOrdered_empty() { 242 try { 243 Hashing.combineOrdered(Collections.<HashCode>emptySet()); 244 fail(); 245 } catch (IllegalArgumentException expected) { 246 } 247 } 248 249 public void testCombineOrdered_differentBitLengths() { 250 try { 251 Hashing.combineOrdered(ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); 252 fail(); 253 } catch (IllegalArgumentException expected) { 254 } 255 } 256 257 public void testCombineOrdered() { 258 HashCode hash31 = HashCode.fromInt(31); 259 HashCode hash32 = HashCode.fromInt(32); 260 assertEquals(hash32, Hashing.combineOrdered(ImmutableList.of(hash32))); 261 assertEquals(HashCode.fromBytes(new byte[] { (byte) 0x80, 0, 0, 0 }), 262 Hashing.combineOrdered(ImmutableList.of(hash32, hash32))); 263 assertEquals(HashCode.fromBytes(new byte[] { (byte) 0xa0, 0, 0, 0 }), 264 Hashing.combineOrdered(ImmutableList.of(hash32, hash32, hash32))); 265 assertFalse( 266 Hashing.combineOrdered(ImmutableList.of(hash31, hash32)).equals( 267 Hashing.combineOrdered(ImmutableList.of(hash32, hash31)))); 268 } 269 270 public void testCombineOrdered_randomHashCodes() { 271 Random random = new Random(7); 272 List<HashCode> hashCodes = Lists.newArrayList(); 273 for (int i = 0; i < 10; i++) { 274 hashCodes.add(HashCode.fromLong(random.nextLong())); 275 } 276 HashCode hashCode1 = Hashing.combineOrdered(hashCodes); 277 Collections.shuffle(hashCodes, random); 278 HashCode hashCode2 = Hashing.combineOrdered(hashCodes); 279 280 assertFalse(hashCode1.equals(hashCode2)); 281 } 282 283 public void testCombineUnordered_empty() { 284 try { 285 Hashing.combineUnordered(Collections.<HashCode>emptySet()); 286 fail(); 287 } catch (IllegalArgumentException expected) { 288 } 289 } 290 291 public void testCombineUnordered_differentBitLengths() { 292 try { 293 Hashing.combineUnordered(ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); 294 fail(); 295 } catch (IllegalArgumentException expected) { 296 } 297 } 298 299 public void testCombineUnordered() { 300 HashCode hash31 = HashCode.fromInt(31); 301 HashCode hash32 = HashCode.fromInt(32); 302 assertEquals(hash32, Hashing.combineUnordered(ImmutableList.of(hash32))); 303 assertEquals(HashCode.fromInt(64), Hashing.combineUnordered(ImmutableList.of(hash32, hash32))); 304 assertEquals(HashCode.fromInt(96), 305 Hashing.combineUnordered(ImmutableList.of(hash32, hash32, hash32))); 306 assertEquals( 307 Hashing.combineUnordered(ImmutableList.of(hash31, hash32)), 308 Hashing.combineUnordered(ImmutableList.of(hash32, hash31))); 309 } 310 311 public void testCombineUnordered_randomHashCodes() { 312 Random random = new Random(RANDOM_SEED); 313 List<HashCode> hashCodes = Lists.newArrayList(); 314 for (int i = 0; i < 10; i++) { 315 hashCodes.add(HashCode.fromLong(random.nextLong())); 316 } 317 HashCode hashCode1 = Hashing.combineUnordered(hashCodes); 318 Collections.shuffle(hashCodes); 319 HashCode hashCode2 = Hashing.combineUnordered(hashCodes); 320 321 assertEquals(hashCode1, hashCode2); 322 } 323 324 public void testConcatenatedHashFunction_equals() { 325 assertEquals( 326 new ConcatenatedHashFunction(Hashing.md5()), 327 new ConcatenatedHashFunction(Hashing.md5())); 328 assertEquals( 329 new ConcatenatedHashFunction(Hashing.md5(), Hashing.murmur3_32()), 330 new ConcatenatedHashFunction(Hashing.md5(), Hashing.murmur3_32())); 331 } 332 333 public void testConcatenatedHashFunction_bits() { 334 assertEquals(Hashing.md5().bits(), 335 new ConcatenatedHashFunction(Hashing.md5()).bits()); 336 assertEquals(Hashing.md5().bits() + Hashing.murmur3_32().bits(), 337 new ConcatenatedHashFunction(Hashing.md5(), Hashing.murmur3_32()).bits()); 338 assertEquals(Hashing.md5().bits() + Hashing.murmur3_32().bits() + Hashing.murmur3_128().bits(), 339 new ConcatenatedHashFunction( 340 Hashing.md5(), Hashing.murmur3_32(), Hashing.murmur3_128()).bits()); 341 } 342 343 public void testConcatenatedHashFunction_makeHash() { 344 byte[] md5Hash = Hashing.md5().hashLong(42L).asBytes(); 345 byte[] murmur3Hash = Hashing.murmur3_32().hashLong(42L).asBytes(); 346 byte[] combined = new byte[md5Hash.length + murmur3Hash.length]; 347 ByteBuffer buffer = ByteBuffer.wrap(combined); 348 buffer.put(md5Hash); 349 buffer.put(murmur3Hash); 350 351 assertEquals(HashCode.fromBytes(combined), 352 new ConcatenatedHashFunction(Hashing.md5(), Hashing.murmur3_32()).hashLong(42L)); 353 } 354 355 public void testHashIntReverseBytesVsHashBytesIntsToByteArray() { 356 int input = 42; 357 assertEquals( 358 Hashing.md5().hashBytes(Ints.toByteArray(input)), 359 Hashing.md5().hashInt(Integer.reverseBytes(input))); 360 } 361 362 public void testHashIntVsForLoop() { 363 int input = 42; 364 HashCode expected = Hashing.md5().hashInt(input); 365 366 Hasher hasher = Hashing.md5().newHasher(); 367 for (int i = 0; i < 32; i += 8) { 368 hasher.putByte((byte) (input >> i)); 369 } 370 HashCode actual = hasher.hash(); 371 372 assertEquals(expected, actual); 373 } 374 375 private static final String EMPTY_STRING = ""; 376 private static final String TQBFJOTLD = "The quick brown fox jumps over the lazy dog"; 377 private static final String TQBFJOTLDP = "The quick brown fox jumps over the lazy dog."; 378 379 private static final ImmutableTable<HashFunction, String, String> KNOWN_HASHES = 380 ImmutableTable.<HashFunction, String, String>builder() 381 .put(Hashing.adler32(), EMPTY_STRING, "01000000") 382 .put(Hashing.adler32(), TQBFJOTLD, "da0fdc5b") 383 .put(Hashing.adler32(), TQBFJOTLDP, "0810e46b") 384 .put(Hashing.md5(), EMPTY_STRING, "d41d8cd98f00b204e9800998ecf8427e") 385 .put(Hashing.md5(), TQBFJOTLD, "9e107d9d372bb6826bd81d3542a419d6") 386 .put(Hashing.md5(), TQBFJOTLDP, "e4d909c290d0fb1ca068ffaddf22cbd0") 387 .put(Hashing.murmur3_128(), EMPTY_STRING, "00000000000000000000000000000000") 388 .put(Hashing.murmur3_128(), TQBFJOTLD, "6c1b07bc7bbc4be347939ac4a93c437a") 389 .put(Hashing.murmur3_128(), TQBFJOTLDP, "c902e99e1f4899cde7b68789a3a15d69") 390 .put(Hashing.murmur3_32(), EMPTY_STRING, "00000000") 391 .put(Hashing.murmur3_32(), TQBFJOTLD, "23f74f2e") 392 .put(Hashing.murmur3_32(), TQBFJOTLDP, "fc8bc4d5") 393 .put(Hashing.sha1(), EMPTY_STRING, "da39a3ee5e6b4b0d3255bfef95601890afd80709") 394 .put(Hashing.sha1(), TQBFJOTLD, "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") 395 .put(Hashing.sha1(), TQBFJOTLDP, "408d94384216f890ff7a0c3528e8bed1e0b01621") 396 .put(Hashing.sha256(), EMPTY_STRING, 397 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 398 .put(Hashing.sha256(), TQBFJOTLD, 399 "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592") 400 .put(Hashing.sha256(), TQBFJOTLDP, 401 "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c") 402 .put(Hashing.sha512(), EMPTY_STRING, 403 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" + 404 "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") 405 .put(Hashing.sha512(), TQBFJOTLD, 406 "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb64" + 407 "2e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6") 408 .put(Hashing.sha512(), TQBFJOTLDP, 409 "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bb" + 410 "c6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed") 411 .put(Hashing.crc32(), EMPTY_STRING, "00000000") 412 .put(Hashing.crc32(), TQBFJOTLD, "39a34f41") 413 .put(Hashing.crc32(), TQBFJOTLDP, "e9259051") 414 .put(Hashing.sipHash24(), EMPTY_STRING, "310e0edd47db6f72") 415 .put(Hashing.sipHash24(), TQBFJOTLD, "e46f1fdc05612752") 416 .put(Hashing.sipHash24(), TQBFJOTLDP, "9b602581fce4d4f8") 417 .put(Hashing.crc32c(), EMPTY_STRING, "00000000") 418 .put(Hashing.crc32c(), TQBFJOTLD, "04046222") 419 .put(Hashing.crc32c(), TQBFJOTLDP, "b3970019") 420 .build(); 421 422 public void testAllHashFunctionsHaveKnownHashes() throws Exception { 423 for (Method method : Hashing.class.getDeclaredMethods()) { 424 if (method.getReturnType().equals(HashFunction.class) // must return HashFunction 425 && Modifier.isPublic(method.getModifiers()) // only the public methods 426 && method.getParameterTypes().length == 0) { // only the seed-less grapes^W hash functions 427 HashFunction hashFunction = (HashFunction) method.invoke(Hashing.class); 428 assertTrue("There should be at least 3 entries in KNOWN_HASHES for " + hashFunction, 429 KNOWN_HASHES.row(hashFunction).size() >= 3); 430 } 431 } 432 } 433 434 public void testKnownUtf8Hashing() { 435 for (Cell<HashFunction, String, String> cell : KNOWN_HASHES.cellSet()) { 436 HashFunction func = cell.getRowKey(); 437 String input = cell.getColumnKey(); 438 String expected = cell.getValue(); 439 assertEquals( 440 String.format("Known hash for hash(%s, UTF_8) failed", input), 441 expected, 442 func.hashString(input, Charsets.UTF_8).toString()); 443 } 444 } 445 446 public void testNullPointers() { 447 NullPointerTester tester = new NullPointerTester() 448 .setDefault(HashCode.class, HashCode.fromLong(0)); 449 tester.testAllPublicStaticMethods(Hashing.class); 450 } 451 452 public void testSeedlessHashFunctionEquals() throws Exception { 453 assertSeedlessHashFunctionEquals(Hashing.class); 454 } 455 456 public void testSeededHashFunctionEquals() throws Exception { 457 assertSeededHashFunctionEquals(Hashing.class); 458 } 459 460 /** 461 * Tests equality of {@link Hashing#goodFastHash} instances. This test must be separate from 462 * {@link #testSeededHashFunctionEquals} because the parameter to {@code goodFastHash} is a size, 463 * not a seed, and because that size is rounded up. Thus, {@code goodFastHash} instances with 464 * different parameters can be equal. That fact is a problem for {@code 465 * testSeededHashFunctionEquals}. 466 */ 467 public void testGoodFastHashEquals() throws Exception { 468 HashFunction hashFunction1a = goodFastHash(1); 469 HashFunction hashFunction1b = goodFastHash(32); 470 HashFunction hashFunction2a = goodFastHash(33); 471 HashFunction hashFunction2b = goodFastHash(128); 472 HashFunction hashFunction3a = goodFastHash(129); 473 HashFunction hashFunction3b = goodFastHash(256); 474 HashFunction hashFunction4a = goodFastHash(257); 475 HashFunction hashFunction4b = goodFastHash(384); 476 477 new EqualsTester() 478 .addEqualityGroup(hashFunction1a, hashFunction1b) 479 .addEqualityGroup(hashFunction2a, hashFunction2b) 480 .addEqualityGroup(hashFunction3a, hashFunction3b) 481 .addEqualityGroup(hashFunction4a, hashFunction4b) 482 .testEquals(); 483 484 assertEquals(hashFunction1a.toString(), hashFunction1b.toString()); 485 assertEquals(hashFunction2a.toString(), hashFunction2b.toString()); 486 assertEquals(hashFunction3a.toString(), hashFunction3b.toString()); 487 assertEquals(hashFunction4a.toString(), hashFunction4b.toString()); 488 } 489 490 static void assertSeedlessHashFunctionEquals(Class<?> clazz) throws Exception { 491 for (Method method : clazz.getDeclaredMethods()) { 492 if (method.getReturnType().equals(HashFunction.class) // must return HashFunction 493 && Modifier.isPublic(method.getModifiers()) // only the public methods 494 && method.getParameterTypes().length == 0) { // only the seed-less hash functions 495 HashFunction hashFunction1a = (HashFunction) method.invoke(clazz); 496 HashFunction hashFunction1b = (HashFunction) method.invoke(clazz); 497 498 new EqualsTester() 499 .addEqualityGroup(hashFunction1a, hashFunction1b) 500 .testEquals(); 501 502 // Make sure we're returning not only equal instances, but constants. 503 assertSame(hashFunction1a, hashFunction1b); 504 505 assertEquals(hashFunction1a.toString(), hashFunction1b.toString()); 506 } 507 } 508 } 509 510 static void assertSeededHashFunctionEquals(Class<?> clazz) throws Exception { 511 Random random = new Random(RANDOM_SEED); 512 for (Method method : clazz.getDeclaredMethods()) { 513 if (method.getReturnType().equals(HashFunction.class) // must return HashFunction 514 && Modifier.isPublic(method.getModifiers()) // only the public methods 515 && method.getParameterTypes().length != 0 // only the seeded hash functions 516 && !method.getName().equals("goodFastHash")) { // tested in testGoodFastHashEquals 517 Object[] params1 = new Object[method.getParameterTypes().length]; 518 Object[] params2 = new Object[method.getParameterTypes().length]; 519 for (int i = 0; i < params1.length; i++) { 520 if (method.getParameterTypes()[i] == int.class) { 521 params1[i] = random.nextInt(); 522 params2[i] = random.nextInt(); 523 } else if (method.getParameterTypes()[i] == long.class) { 524 params1[i] = random.nextLong(); 525 params2[i] = random.nextLong(); 526 } else { 527 fail("Unable to create a random parameter for " + method.getParameterTypes()[i]); 528 } 529 } 530 HashFunction hashFunction1a = (HashFunction) method.invoke(clazz, params1); 531 HashFunction hashFunction1b = (HashFunction) method.invoke(clazz, params1); 532 HashFunction hashFunction2 = (HashFunction) method.invoke(clazz, params2); 533 534 new EqualsTester() 535 .addEqualityGroup(hashFunction1a, hashFunction1b) 536 .addEqualityGroup(hashFunction2) 537 .testEquals(); 538 539 assertEquals(hashFunction1a.toString(), hashFunction1b.toString()); 540 } 541 } 542 } 543 } 544