/* * Copyright 2017 The gRPC 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 io.grpc; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import io.grpc.PersistentHashArrayMappedTrie.CollisionLeaf; import io.grpc.PersistentHashArrayMappedTrie.CompressedIndex; import io.grpc.PersistentHashArrayMappedTrie.Leaf; import io.grpc.PersistentHashArrayMappedTrie.Node; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class PersistentHashArrayMappedTrieTest { @Test public void leaf_replace() { Key key = new Key(0); Object value1 = new Object(); Object value2 = new Object(); Leaf leaf = new Leaf(key, value1); Node ret = leaf.put(key, value2, key.hashCode(), 0); assertTrue(ret instanceof Leaf); assertSame(value2, ret.get(key, key.hashCode(), 0)); assertSame(value1, leaf.get(key, key.hashCode(), 0)); assertEquals(1, leaf.size()); assertEquals(1, ret.size()); } @Test public void leaf_collision() { Key key1 = new Key(0); Key key2 = new Key(0); Object value1 = new Object(); Object value2 = new Object(); Leaf leaf = new Leaf(key1, value1); Node ret = leaf.put(key2, value2, key2.hashCode(), 0); assertTrue(ret instanceof CollisionLeaf); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertSame(value1, leaf.get(key1, key1.hashCode(), 0)); assertSame(null, leaf.get(key2, key2.hashCode(), 0)); assertEquals(1, leaf.size()); assertEquals(2, ret.size()); } @Test public void leaf_insert() { Key key1 = new Key(0); Key key2 = new Key(1); Object value1 = new Object(); Object value2 = new Object(); Leaf leaf = new Leaf(key1, value1); Node ret = leaf.put(key2, value2, key2.hashCode(), 0); assertTrue(ret instanceof CompressedIndex); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertSame(value1, leaf.get(key1, key1.hashCode(), 0)); assertSame(null, leaf.get(key2, key2.hashCode(), 0)); assertEquals(1, leaf.size()); assertEquals(2, ret.size()); } @Test(expected = AssertionError.class) public void collisionLeaf_assertKeysDifferent() { Key key1 = new Key(0); new CollisionLeaf(key1, new Object(), key1, new Object()); } @Test(expected = AssertionError.class) public void collisionLeaf_assertHashesSame() { new CollisionLeaf(new Key(0), new Object(), new Key(1), new Object()); } @Test public void collisionLeaf_insert() { Key key1 = new Key(0); Key key2 = new Key(key1.hashCode()); Key insertKey = new Key(1); Object value1 = new Object(); Object value2 = new Object(); Object insertValue = new Object(); CollisionLeaf leaf = new CollisionLeaf(key1, value1, key2, value2); Node ret = leaf.put(insertKey, insertValue, insertKey.hashCode(), 0); assertTrue(ret instanceof CompressedIndex); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertSame(insertValue, ret.get(insertKey, insertKey.hashCode(), 0)); assertSame(value1, leaf.get(key1, key1.hashCode(), 0)); assertSame(value2, leaf.get(key2, key2.hashCode(), 0)); assertSame(null, leaf.get(insertKey, insertKey.hashCode(), 0)); assertEquals(2, leaf.size()); assertEquals(3, ret.size()); } @Test public void collisionLeaf_replace() { Key replaceKey = new Key(0); Object originalValue = new Object(); Key key = new Key(replaceKey.hashCode()); Object value = new Object(); CollisionLeaf leaf = new CollisionLeaf(replaceKey, originalValue, key, value); Object replaceValue = new Object(); Node ret = leaf.put(replaceKey, replaceValue, replaceKey.hashCode(), 0); assertTrue(ret instanceof CollisionLeaf); assertSame(replaceValue, ret.get(replaceKey, replaceKey.hashCode(), 0)); assertSame(value, ret.get(key, key.hashCode(), 0)); assertSame(value, leaf.get(key, key.hashCode(), 0)); assertSame(originalValue, leaf.get(replaceKey, replaceKey.hashCode(), 0)); assertEquals(2, leaf.size()); assertEquals(2, ret.size()); } @Test public void collisionLeaf_collision() { Key key1 = new Key(0); Key key2 = new Key(key1.hashCode()); Key key3 = new Key(key1.hashCode()); Object value1 = new Object(); Object value2 = new Object(); Object value3 = new Object(); CollisionLeaf leaf = new CollisionLeaf(key1, value1, key2, value2); Node ret = leaf.put(key3, value3, key3.hashCode(), 0); assertTrue(ret instanceof CollisionLeaf); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertSame(value3, ret.get(key3, key3.hashCode(), 0)); assertSame(value1, leaf.get(key1, key1.hashCode(), 0)); assertSame(value2, leaf.get(key2, key2.hashCode(), 0)); assertSame(null, leaf.get(key3, key3.hashCode(), 0)); assertEquals(2, leaf.size()); assertEquals(3, ret.size()); } @Test public void compressedIndex_combine_differentIndexBit() { final Key key1 = new Key(7); final Key key2 = new Key(19); final Object value1 = new Object(); final Object value2 = new Object(); Leaf leaf1 = new Leaf(key1, value1); Leaf leaf2 = new Leaf(key2, value2); class Verifier { private void verify(Node ret) { CompressedIndex collisionLeaf = (CompressedIndex) ret; assertEquals((1 << 7) | (1 << 19), collisionLeaf.bitmap); assertEquals(2, collisionLeaf.values.length); assertSame(value1, collisionLeaf.values[0].get(key1, key1.hashCode(), 0)); assertSame(value2, collisionLeaf.values[1].get(key2, key2.hashCode(), 0)); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertEquals(2, ret.size()); } } Verifier verifier = new Verifier(); verifier.verify(CompressedIndex.combine(leaf1, key1.hashCode(), leaf2, key2.hashCode(), 0)); verifier.verify(CompressedIndex.combine(leaf2, key2.hashCode(), leaf1, key1.hashCode(), 0)); assertEquals(1, leaf1.size()); assertEquals(1, leaf2.size()); } @Test public void compressedIndex_combine_sameIndexBit() { final Key key1 = new Key(17 << 5 | 1); // 5 bit regions: (17, 1) final Key key2 = new Key(31 << 5 | 1); // 5 bit regions: (31, 1) final Object value1 = new Object(); final Object value2 = new Object(); Leaf leaf1 = new Leaf(key1, value1); Leaf leaf2 = new Leaf(key2, value2); class Verifier { private void verify(Node ret) { CompressedIndex collisionInternal = (CompressedIndex) ret; assertEquals(1 << 1, collisionInternal.bitmap); assertEquals(1, collisionInternal.values.length); CompressedIndex collisionLeaf = (CompressedIndex) collisionInternal.values[0]; assertEquals((1 << 31) | (1 << 17), collisionLeaf.bitmap); assertSame(value1, ret.get(key1, key1.hashCode(), 0)); assertSame(value2, ret.get(key2, key2.hashCode(), 0)); assertEquals(2, ret.size()); } } Verifier verifier = new Verifier(); verifier.verify(CompressedIndex.combine(leaf1, key1.hashCode(), leaf2, key2.hashCode, 0)); verifier.verify(CompressedIndex.combine(leaf2, key2.hashCode(), leaf1, key1.hashCode, 0)); assertEquals(1, leaf1.size()); assertEquals(1, leaf2.size()); } /** * A key with a settable hashcode. */ static final class Key { private final int hashCode; Key(int hashCode) { this.hashCode = hashCode; } @Override public int hashCode() { return hashCode; } @Override public String toString() { return String.format("Key(hashCode=%x)", hashCode); } } }