Home | History | Annotate | Download | only in hash
      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