/* * 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.collect; import static com.google.common.collect.BstSide.LEFT; import static com.google.common.collect.BstTesting.assertInOrderTraversalIs; import static com.google.common.collect.BstTesting.balancePolicy; import static com.google.common.collect.BstTesting.defaultNullPointerTester; import static com.google.common.collect.BstTesting.extension; import static com.google.common.collect.BstTesting.nodeFactory; import static com.google.common.collect.BstTesting.pathFactory; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.isNull; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reportMatcher; import static org.easymock.EasyMock.same; import static org.easymock.EasyMock.verify; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.BstModificationResult.ModificationType; import com.google.common.collect.BstTesting.SimpleNode; import junit.framework.TestCase; import org.easymock.EasyMock; import org.easymock.IArgumentMatcher; /** * Tests for {@code BstOperations}. * * @author Louis Wasserman */ @GwtCompatible(emulated = true) public class BstOperationsTest extends TestCase { public void testSeek1() { // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); assertEquals(a, BstOperations.seek(Ordering.natural(), d, 'a')); assertEquals(b, BstOperations.seek(Ordering.natural(), d, 'b')); assertNull(BstOperations.seek(Ordering.natural(), d, 'c')); assertEquals(d, BstOperations.seek(Ordering.natural(), d, 'd')); assertNull(BstOperations.seek(Ordering.natural(), d, 'e')); assertEquals(f, BstOperations.seek(Ordering.natural(), d, 'f')); assertEquals(g, BstOperations.seek(Ordering.natural(), d, 'g')); } public void testSeek2() { // d // / \ // b f // \ / // c e SimpleNode c = new SimpleNode('c', null, null); SimpleNode b = new SimpleNode('b', null, c); SimpleNode e = new SimpleNode('e', null, null); SimpleNode f = new SimpleNode('f', e, null); SimpleNode d = new SimpleNode('d', b, f); assertNull(BstOperations.seek(Ordering.natural(), d, 'a')); assertEquals(b, BstOperations.seek(Ordering.natural(), d, 'b')); assertEquals(c, BstOperations.seek(Ordering.natural(), d, 'c')); assertEquals(d, BstOperations.seek(Ordering.natural(), d, 'd')); assertEquals(e, BstOperations.seek(Ordering.natural(), d, 'e')); assertEquals(f, BstOperations.seek(Ordering.natural(), d, 'f')); assertNull(BstOperations.seek(Ordering.natural(), d, 'g')); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyInsertAbsentNode() { // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); SimpleNode c = new SimpleNode('c', null, null); expect(modifier.modify(eq('c'), (SimpleNode) isNull())).andReturn( BstModificationResult.rebalancingChange(null, c)); expect(balancePolicy.balance( same(nodeFactory), same(c), (SimpleNode) isNull(), (SimpleNode) isNull())) .andReturn(c) .times(0, 1); SimpleNode bWithC = new SimpleNode('b', a, c); expectPossibleEntryfication(nodeFactory, b); expect(balancePolicy.balance( same(nodeFactory), withKey('b'), same(a), withKey('c'))) .andReturn(bWithC); SimpleNode dWithBWithC = new SimpleNode('d', bWithC, f); expectPossibleEntryfication(nodeFactory, d); expect( balancePolicy.balance(same(nodeFactory), withKey('d'), same(bWithC), same(f))) .andReturn(dWithBWithC); replay(nodeFactory, balancePolicy, modifier); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(Ordering.natural(), mutationRule, d, 'c'); assertEquals('c', mutationResult.getTargetKey().charValue()); assertNull(mutationResult.getOriginalTarget()); assertEquals('c', mutationResult .getChangedTarget() .getKey() .charValue()); assertSame(d, mutationResult.getOriginalRoot()); assertSame(dWithBWithC, mutationResult.getChangedRoot()); assertEquals(ModificationType.REBALANCING_CHANGE, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyInsertPresentNode() { // We wish to test that BstOperations & co. treat IDENTITY modifications as the same. // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); expectPossibleEntryfication(nodeFactory, a); expect(modifier.modify(eq('a'), withKey('a'))).andReturn( BstModificationResult.identity(a)); replay(nodeFactory, balancePolicy, modifier); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(Ordering.natural(), mutationRule, d, 'a'); assertEquals('a', mutationResult.getTargetKey().charValue()); assertSame(a, mutationResult.getOriginalTarget()); assertSame(a, mutationResult.getChangedTarget()); assertSame(d, mutationResult.getOriginalRoot()); assertSame(d, mutationResult.getChangedRoot()); assertEquals(ModificationType.IDENTITY, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyInsertInequivalentNode() { // We wish to test that BstOperations & co. treat non-equivalent() nodes as different. // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); expectPossibleEntryfication(nodeFactory, a); SimpleNode a2 = new SimpleNode('a', null, null); expect(modifier.modify(eq('a'), withKey('a'))).andReturn( BstModificationResult.rebuildingChange(a, a2)); expectPossibleEntryfication(nodeFactory, a2); SimpleNode bWithA2 = new SimpleNode('b', a2, null); expect(nodeFactory.createNode(same(b), withKey('a'), (SimpleNode) isNull())).andReturn( bWithA2); SimpleNode dWithA2 = new SimpleNode('d', bWithA2, f); expect(nodeFactory.createNode(same(d), same(bWithA2), same(f))).andReturn( dWithA2); replay(nodeFactory, balancePolicy, modifier); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(Ordering.natural(), mutationRule, d, 'a'); assertEquals('a', mutationResult.getTargetKey().charValue()); assertSame(a, mutationResult.getOriginalTarget()); assertEquals('a', mutationResult.getChangedTarget().getKey().charValue()); assertSame(d, mutationResult.getOriginalRoot()); assertSame(dWithA2, mutationResult.getChangedRoot()); assertEquals(ModificationType.REBUILDING_CHANGE, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyDeletePresentNode() { // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); expectPossibleEntryfication(nodeFactory, a); expect(modifier.modify(eq('a'), withKey('a'))).andReturn( BstModificationResult.rebalancingChange(a, null)); expect(balancePolicy.combine(same(nodeFactory), (SimpleNode) isNull(), (SimpleNode) isNull())) .andReturn(null); expectPossibleEntryfication(nodeFactory, b); SimpleNode leafB = new SimpleNode('b', null, null); expect( balancePolicy.balance(same(nodeFactory), withKey('b'), (SimpleNode) isNull(), (SimpleNode) isNull())).andReturn(leafB); SimpleNode dWithLeafB = new SimpleNode('d', leafB, f); expectPossibleEntryfication(nodeFactory, d); expect( balancePolicy.balance(same(nodeFactory), withKey('d'), same(leafB), same(f))) .andReturn(dWithLeafB); replay(nodeFactory, balancePolicy, modifier); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(Ordering.natural(), mutationRule, d, 'a'); assertEquals('a', mutationResult.getTargetKey().charValue()); assertEquals('a', mutationResult .getOriginalTarget() .getKey() .charValue()); assertNull(mutationResult.getChangedTarget()); assertSame(d, mutationResult.getOriginalRoot()); assertSame(dWithLeafB, mutationResult.getChangedRoot()); assertEquals(ModificationType.REBALANCING_CHANGE, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyDeleteAbsentNode() { // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); expectPossibleEntryfication(nodeFactory, a); expect(modifier.modify(eq('c'), (SimpleNode) isNull())).andReturn( BstModificationResult.<SimpleNode> identity(null)); replay(nodeFactory, balancePolicy, modifier); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(Ordering.natural(), mutationRule, d, 'c'); assertEquals('c', mutationResult.getTargetKey().charValue()); assertEquals(d, mutationResult.getOriginalRoot()); assertEquals(d, mutationResult.getChangedRoot()); assertNull(mutationResult.getOriginalTarget()); assertNull(mutationResult.getChangedTarget()); assertEquals(ModificationType.IDENTITY, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") @SuppressWarnings("unchecked") public void testModifyPathInsertPresentNode() { // We wish to test that BstOperations & co. treat identity-different nodes as changed, // instead of using SimpleNode.equals(). // d // / \ // b f // / \ // a g SimpleNode a = new SimpleNode('a', null, null); SimpleNode b = new SimpleNode('b', a, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstNodeFactory<SimpleNode> nodeFactory = EasyMock.createStrictMock(BstNodeFactory.class); BstBalancePolicy<SimpleNode> balancePolicy = EasyMock.createStrictMock(BstBalancePolicy.class); BstModifier<Character, SimpleNode> modifier = EasyMock.createStrictMock(BstModifier.class); expectPossibleEntryfication(nodeFactory, a); expect(modifier.modify(eq('a'), withKey('a'))).andReturn(BstModificationResult.identity(a)); replay(nodeFactory, balancePolicy, modifier); BstInOrderPath<SimpleNode> path = extension(pathFactory, d, LEFT, LEFT); BstMutationRule<Character, SimpleNode> mutationRule = BstMutationRule.createRule(modifier, balancePolicy, nodeFactory); BstMutationResult<Character, SimpleNode> mutationResult = BstOperations.mutate(path, mutationRule); assertEquals('a', mutationResult.getTargetKey().charValue()); assertSame(a, mutationResult.getOriginalTarget()); assertSame(a, mutationResult.getChangedTarget()); assertSame(d, mutationResult.getOriginalRoot()); assertSame(d, mutationResult.getChangedRoot()); assertEquals(ModificationType.IDENTITY, mutationResult.modificationType()); verify(nodeFactory, balancePolicy, modifier); } @GwtIncompatible("EasyMock") private SimpleNode withKey(final char c) { reportMatcher(new IArgumentMatcher() { @Override public void appendTo(StringBuffer buffer) { buffer.append("Expected BstNode with key ").append(c); } @Override public boolean matches(Object argument) { return argument instanceof SimpleNode && ((SimpleNode) argument).getKey().charValue() == c; } }); return null; } /** * The implementation may remove the children of a node it treats as an entry for safety. Expect * this and handle it. */ @GwtIncompatible("EasyMock") private void expectPossibleEntryfication(BstNodeFactory<SimpleNode> factory, SimpleNode entry) { expect(factory.createNode(same(entry), (SimpleNode) isNull(), (SimpleNode) isNull())) .andReturn(new SimpleNode(entry.getKey(), null, null)) .times(0, 1); } public void testInsertMin1() { // d // / \ // b f // \ \ // c g SimpleNode c = new SimpleNode('c', null, null); SimpleNode b = new SimpleNode('b', null, c); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); SimpleNode a = new SimpleNode('a', null, null); SimpleNode newRoot = BstOperations.insertMin(d, a, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "abcdfg"); } public void testInsertMin2() { // d // / \ // b f // \ // g SimpleNode b = new SimpleNode('b', null, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); SimpleNode a = new SimpleNode('a', null, null); SimpleNode newRoot = BstOperations.insertMin(d, a, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "abdfg"); } public void testInsertMinEmpty() { SimpleNode a = new SimpleNode('a', null, null); SimpleNode newRoot = BstOperations.insertMin(null, a, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "a"); } public void testInsertMax1() { // d // / \ // b f // \ \ // c g SimpleNode c = new SimpleNode('c', null, null); SimpleNode b = new SimpleNode('b', null, c); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); SimpleNode h = new SimpleNode('h', null, null); SimpleNode newRoot = BstOperations.insertMax(d, h, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "bcdfgh"); } public void testInsertMax2() { // d // / \ // b f // / // e SimpleNode b = new SimpleNode('b', null, null); SimpleNode e = new SimpleNode('e', null, null); SimpleNode f = new SimpleNode('f', e, null); SimpleNode d = new SimpleNode('d', b, f); SimpleNode h = new SimpleNode('h', null, null); SimpleNode newRoot = BstOperations.insertMax(d, h, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "bdefh"); } public void testInsertMaxEmpty() { SimpleNode a = new SimpleNode('a', null, null); SimpleNode newRoot = BstOperations.insertMax(null, a, nodeFactory, balancePolicy); assertInOrderTraversalIs(newRoot, "a"); } public void testExtractMin1() { // d // / \ // b f // \ \ // c g SimpleNode c = new SimpleNode('c', null, null); SimpleNode b = new SimpleNode('b', null, c); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstMutationResult<Character, SimpleNode> extractMin = BstOperations.extractMin(d, nodeFactory, balancePolicy); assertEquals('b', extractMin.getTargetKey().charValue()); assertEquals(d, extractMin.getOriginalRoot()); assertEquals(b, extractMin.getOriginalTarget()); assertNull(extractMin.getChangedTarget()); assertInOrderTraversalIs(extractMin.getChangedRoot(), "cdfg"); } public void testExtractMin2() { // d // / \ // b f // \ // g SimpleNode b = new SimpleNode('b', null, null); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstMutationResult<Character, SimpleNode> extractMin = BstOperations.extractMin(d, nodeFactory, balancePolicy); assertEquals('b', extractMin.getTargetKey().charValue()); assertEquals(d, extractMin.getOriginalRoot()); assertEquals(b, extractMin.getOriginalTarget()); assertNull(extractMin.getChangedTarget()); assertInOrderTraversalIs(extractMin.getChangedRoot(), "dfg"); } public void testExtractMax1() { // d // / \ // b f // \ \ // c g SimpleNode c = new SimpleNode('c', null, null); SimpleNode b = new SimpleNode('b', null, c); SimpleNode g = new SimpleNode('g', null, null); SimpleNode f = new SimpleNode('f', null, g); SimpleNode d = new SimpleNode('d', b, f); BstMutationResult<Character, SimpleNode> extractMax = BstOperations.extractMax(d, nodeFactory, balancePolicy); assertEquals('g', extractMax.getTargetKey().charValue()); assertEquals(d, extractMax.getOriginalRoot()); assertEquals(g, extractMax.getOriginalTarget()); assertNull(extractMax.getChangedTarget()); assertInOrderTraversalIs(extractMax.getChangedRoot(), "bcdf"); } public void testExtractMax2() { // d // / \ // b f // / // e SimpleNode b = new SimpleNode('b', null, null); SimpleNode e = new SimpleNode('e', null, null); SimpleNode f = new SimpleNode('f', e, null); SimpleNode d = new SimpleNode('d', b, f); BstMutationResult<Character, SimpleNode> extractMax = BstOperations.extractMax(d, nodeFactory, balancePolicy); assertEquals('f', extractMax.getTargetKey().charValue()); assertEquals(d, extractMax.getOriginalRoot()); assertEquals(f, extractMax.getOriginalTarget()); assertNull(extractMax.getChangedTarget()); assertInOrderTraversalIs(extractMax.getChangedRoot(), "bde"); } @GwtIncompatible("NullPointerTester") public void testNullPointers() throws Exception { defaultNullPointerTester().testAllPublicStaticMethods(BstOperations.class); } }