/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.sql.optimizer.rule; import com.foundationdb.util.AssertUtils; import org.junit.Test; import java.util.Collections; import java.util.HashSet; import java.util.Set; import static org.junit.Assert.assertEquals; public class EquivalenceFinderTest { @Test public void identity() { check(create() , true, 1, 1); } @Test public void notEquivalent() { check(create(), false, 1, 2); } @Test public void simple() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); check(finder, true, 1, 2); } @Test public void transitive() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); check(finder, true, 1, 2); check(finder, true, 1, 3); check(finder, true, 2, 3); } @Test public void transitiveBushy() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); finder.markEquivalent(2, 4); finder.markEquivalent(2, 5); finder.markEquivalent(5, 6); check(finder, true, 1, 6); } @Test public void loopedOneApart() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 1); check(finder, true, 1, 2); } @Test public void loopedTwoApart() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); finder.markEquivalent(3, 1); check(finder, true, 1, 2); check(finder, true, 1, 3); check(finder, true, 2, 3); } @Test public void traverseBarelyWorks() { EquivalenceFinder<Integer> finder = create(6); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); finder.markEquivalent(3, 4); finder.markEquivalent(4, 5); finder.markEquivalent(5, 6); check(finder, true, 1, 6); } @Test public void traverseBarelyFails() { EquivalenceFinder<Integer> finder = create(4); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); finder.markEquivalent(3, 4); finder.markEquivalent(4, 5); finder.markEquivalent(5, 6); check(finder, null, 1, 6); } @Test public void emptyEquivalenceSet() { EquivalenceFinder<Integer> finder = create(); checkEquivalents(1, finder); } @Test public void noneEquivalenceSet() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(2, 3); checkEquivalents(1, finder); } @Test public void someEquivalenceSet() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); checkEquivalents(1, finder, 2, 3); } @Test public void loopedEquivalenceSet() { EquivalenceFinder<Integer> finder = create(); finder.markEquivalent(1, 2); finder.markEquivalent(2, 3); finder.markEquivalent(3, 1); checkEquivalents(1, finder, 2, 3); } @Test(expected = IllegalArgumentException.class) public void nullEquivalence() { create().markEquivalent(1, null); } protected static void checkEquivalents(Integer from, EquivalenceFinder<? super Integer> finder, Integer... expected) { Set<Integer> expectedSet = new HashSet<>(); Collections.addAll(expectedSet, expected); AssertUtils.assertCollectionEquals("equivalents for " + from, expectedSet, finder.findEquivalents(from)); } private static <T> void check(EquivalenceFinder<? super T> finder, Boolean expected, T one, T two) { checkOneDirection(finder, expected, one, two); checkOneDirection(finder, expected, two, one); } private static <T> void checkOneDirection(EquivalenceFinder<? super T> finder, Boolean expected, T one, T two) { Boolean result; try { result = finder.areEquivalent(one, two); } catch (TooMuchTraversingException e) { result = null; } assertEquals("equivalence of " + one + ", " + two, expected, result); } private static EquivalenceFinder<Integer> create() { return new TraversalBoundEquivalenceFinder<>(); } private static EquivalenceFinder<Integer> create(int maxTraversal) { return new TraversalBoundEquivalenceFinder<>(maxTraversal); } private static class TooMuchTraversingException extends RuntimeException { } private static class TraversalBoundEquivalenceFinder<Integer> extends EquivalenceFinder<Integer> { private TraversalBoundEquivalenceFinder() { } public TraversalBoundEquivalenceFinder(int maxTraversal) { super(maxTraversal); } @Override void tooMuchTraversing() { throw new TooMuchTraversingException(); } } }