/* * Copyright (C) 2011 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.hash; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.util.concurrent.AtomicLongMap; import junit.framework.TestCase; import java.util.Collections; import java.util.List; import java.util.Random; /** * Unit tests for functions of {@code Hashing} that don't have their own tests. */ public class HashingTest extends TestCase { public void testPadToLong() { assertEquals(0x1111111111111111L, Hashing.padToLong(HashCodes.fromLong(0x1111111111111111L))); assertEquals(0x9999999999999999L, Hashing.padToLong(HashCodes.fromLong(0x9999999999999999L))); assertEquals(0x0000000011111111L, Hashing.padToLong(HashCodes.fromInt(0x11111111))); assertEquals(0x0000000099999999L, Hashing.padToLong(HashCodes.fromInt(0x99999999))); } public void testConsistentHash_correctness() { long[] interestingValues = { -1, 0, 1, 2, Long.MAX_VALUE, Long.MIN_VALUE }; for (long h : interestingValues) { checkConsistentHashCorrectness(h); } Random r = new Random(7); for (int i = 0; i < 20; i++) { checkConsistentHashCorrectness(r.nextLong()); } } private void checkConsistentHashCorrectness(long hashCode) { int last = 0; for (int shards = 1; shards <= 100000; shards++) { int b = Hashing.consistentHash(hashCode, shards); if (b != last) { assertEquals(shards - 1, b); last = b; } } } public void testConsistentHash_probabilities() { AtomicLongMap map = AtomicLongMap.create(); Random r = new Random(9); for (int i = 0; i < ITERS; i++) { countRemaps(r.nextLong(), map); } for (int shard = 2; shard <= MAX_SHARDS; shard++) { // Rough: don't exceed 1.2x the expected number of remaps by more than 20 assertTrue(map.get(shard) <= 1.2 * ITERS / shard + 20); } } private void countRemaps(long h, AtomicLongMap map) { int last = 0; for (int shards = 2; shards <= MAX_SHARDS; shards++) { int chosen = Hashing.consistentHash(h, shards); if (chosen != last) { map.incrementAndGet(shards); last = chosen; } } } private static final int ITERS = 10000; private static final int MAX_SHARDS = 500; public void testConsistentHash_outOfRange() { try { Hashing.consistentHash(5L, 0); fail(); } catch (IllegalArgumentException expected) { } } public void testConsistentHash_ofHashCode() { checkSameResult(HashCodes.fromLong(1), 1); checkSameResult(HashCodes.fromLong(0x9999999999999999L), 0x9999999999999999L); checkSameResult(HashCodes.fromInt(0x99999999), 0x0000000099999999L); } public void checkSameResult(HashCode hashCode, long equivLong) { assertEquals(Hashing.consistentHash(equivLong, 5555), Hashing.consistentHash(hashCode, 5555)); } public void testCombineOrdered_null() { try { Hashing.combineOrdered(null); fail(); } catch (NullPointerException expected) { } } public void testCombineOrdered_empty() { try { Hashing.combineOrdered(Collections.emptySet()); fail(); } catch (IllegalArgumentException expected) { } } public void testCombineOrdered_differentBitLengths() { try { Hashing.combineOrdered(ImmutableList.of(HashCodes.fromInt(32), HashCodes.fromLong(32L))); fail(); } catch (IllegalArgumentException expected) { } } public void testCombineOrdered() { HashCode hash31 = HashCodes.fromInt(31); HashCode hash32 = HashCodes.fromInt(32); assertEquals(hash32, Hashing.combineOrdered(ImmutableList.of(hash32))); assertEquals(HashCodes.fromBytes(new byte[] { (byte) 0x80, 0, 0, 0 }), Hashing.combineOrdered(ImmutableList.of(hash32, hash32))); assertEquals(HashCodes.fromBytes(new byte[] { (byte) 0xa0, 0, 0, 0 }), Hashing.combineOrdered(ImmutableList.of(hash32, hash32, hash32))); assertFalse( Hashing.combineOrdered(ImmutableList.of(hash31, hash32)).equals( Hashing.combineOrdered(ImmutableList.of(hash32, hash31)))); } public void testCombineOrdered_randomHashCodes() { Random random = new Random(7); List hashCodes = Lists.newArrayList(); for (int i = 0; i < 10; i++) { hashCodes.add(HashCodes.fromLong(random.nextLong())); } HashCode hashCode1 = Hashing.combineOrdered(hashCodes); Collections.shuffle(hashCodes, random); HashCode hashCode2 = Hashing.combineOrdered(hashCodes); assertFalse(hashCode1.equals(hashCode2)); } public void testCombineUnordered_null() { try { Hashing.combineUnordered(null); fail(); } catch (NullPointerException expected) { } } public void testCombineUnordered_empty() { try { Hashing.combineUnordered(Collections.emptySet()); fail(); } catch (IllegalArgumentException expected) { } } public void testCombineUnordered_differentBitLengths() { try { Hashing.combineUnordered(ImmutableList.of(HashCodes.fromInt(32), HashCodes.fromLong(32L))); fail(); } catch (IllegalArgumentException expected) { } } public void testCombineUnordered() { HashCode hash31 = HashCodes.fromInt(31); HashCode hash32 = HashCodes.fromInt(32); assertEquals(hash32, Hashing.combineUnordered(ImmutableList.of(hash32))); assertEquals(HashCodes.fromInt(64), Hashing.combineUnordered(ImmutableList.of(hash32, hash32))); assertEquals(HashCodes.fromInt(96), Hashing.combineUnordered(ImmutableList.of(hash32, hash32, hash32))); assertEquals( Hashing.combineUnordered(ImmutableList.of(hash31, hash32)), Hashing.combineUnordered(ImmutableList.of(hash32, hash31))); } public void testCombineUnordered_randomHashCodes() { Random random = new Random(); List hashCodes = Lists.newArrayList(); for (int i = 0; i < 10; i++) { hashCodes.add(HashCodes.fromLong(random.nextLong())); } HashCode hashCode1 = Hashing.combineUnordered(hashCodes); Collections.shuffle(hashCodes); HashCode hashCode2 = Hashing.combineUnordered(hashCodes); assertEquals(hashCode1, hashCode2); } }