--- /dev/null 2013-05-28 12:47:12.000000000 -0700 +++ new/test/java/util/Spliterator/SpliteratorCollisions.java 2013-05-28 12:47:12.000000000 -0700 @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8005698 + * @run testng SpliteratorCollisions + * @summary Spliterator traversing and splitting hash maps containing colliding hashes + * @author Brent Christian + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Spliterator; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.LongConsumer; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; + +@Test +public class SpliteratorCollisions { + + private static List SIZES = Arrays.asList(0, 1, 10, 100, 1000); + + private static class SpliteratorDataBuilder { + List data; + List exp; + Map mExp; + + SpliteratorDataBuilder(List data, List exp) { + this.data = data; + this.exp = exp; + this.mExp = createMap(exp); + } + + Map createMap(List l) { + Map m = new LinkedHashMap<>(); + for (T t : l) { + m.put(t, t); + } + return m; + } + + void add(String description, Collection expected, Supplier> s) { + description = joiner(description).toString(); + data.add(new Object[]{description, expected, s}); + } + + void add(String description, Supplier> s) { + add(description, exp, s); + } + + void addCollection(Function, ? extends Collection> c) { + add("new " + c.apply(Collections.emptyList()).getClass().getName() + ".spliterator()", + () -> c.apply(exp).spliterator()); + } + + void addList(Function, ? extends List> l) { + // @@@ If collection is instance of List then add sub-list tests + addCollection(l); + } + + void addMap(Function, ? extends Map> m) { + String description = "new " + m.apply(Collections.emptyMap()).getClass().getName(); + add(description + ".keySet().spliterator()", () -> m.apply(mExp).keySet().spliterator()); + add(description + ".values().spliterator()", () -> m.apply(mExp).values().spliterator()); + add(description + ".entrySet().spliterator()", mExp.entrySet(), () -> m.apply(mExp).entrySet().spliterator()); + } + + StringBuilder joiner(String description) { + return new StringBuilder(description). + append(" {"). + append("size=").append(exp.size()). + append("}"); + } + } + + static Object[][] spliteratorDataProvider; + + @DataProvider(name = "HashableIntSpliterator") + public static Object[][] spliteratorDataProvider() { + if (spliteratorDataProvider != null) { + return spliteratorDataProvider; + } + + List data = new ArrayList<>(); + for (int size : SIZES) { + List exp = listIntRange(size, false); + SpliteratorDataBuilder db = new SpliteratorDataBuilder<>(data, exp); + + // Maps + db.addMap(HashMap::new); + db.addMap(LinkedHashMap::new); + + // Collections that use HashMap + db.addCollection(HashSet::new); + db.addCollection(LinkedHashSet::new); + db.addCollection(TreeSet::new); + } + return spliteratorDataProvider = data.toArray(new Object[0][]); + } + + static Object[][] spliteratorDataProviderWithNull; + + @DataProvider(name = "HashableIntSpliteratorWithNull") + public static Object[][] spliteratorNullDataProvider() { + if (spliteratorDataProviderWithNull != null) { + return spliteratorDataProviderWithNull; + } + + List data = new ArrayList<>(); + for (int size : SIZES) { + List exp = listIntRange(size, true); + exp.add(0, null); + SpliteratorDataBuilder db = new SpliteratorDataBuilder<>(data, exp); + + // Maps + db.addMap(HashMap::new); + db.addMap(LinkedHashMap::new); + // TODO: add this back in if we decide to keep TreeBin in WeakHashMap + //db.addMap(WeakHashMap::new); + + // Collections that use HashMap + db.addCollection(HashSet::new); + db.addCollection(LinkedHashSet::new); +// db.addCollection(TreeSet::new); + + } + return spliteratorDataProviderWithNull = data.toArray(new Object[0][]); + } + + final static class HashableInteger implements Comparable { + + final int value; + final int hashmask; //yes duplication + + HashableInteger(int value, int hashmask) { + this.value = value; + this.hashmask = hashmask; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof HashableInteger) { + HashableInteger other = (HashableInteger) obj; + + return other.value == value; + } + + return false; + } + + @Override + public int hashCode() { + return value % hashmask; + } + + @Override + public int compareTo(HashableInteger o) { + return value - o.value; + } + + @Override + public String toString() { + return Integer.toString(value); + } + } + + private static List listIntRange(int upTo, boolean withNull) { + List exp = new ArrayList<>(); + if (withNull) { + exp.add(null); + } + for (int i = 0; i < upTo; i++) { + exp.add(new HashableInteger(i, 10)); + } + return Collections.unmodifiableList(exp); + } + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testNullPointerException(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null)); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testNullPointerExceptionWithNull(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null)); + } + + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testForEach(String description, Collection exp, Supplier s) { + testForEach(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testForEachWithNull(String description, Collection exp, Supplier s) { + testForEach(exp, s, (Consumer b) -> b); + } + + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testTryAdvance(String description, Collection exp, Supplier s) { + testTryAdvance(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testTryAdvanceWithNull(String description, Collection exp, Supplier s) { + testTryAdvance(exp, s, (Consumer b) -> b); + } + +/* skip this test until 8013649 is fixed + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testMixedTryAdvanceForEach(String description, Collection exp, Supplier s) { + testMixedTryAdvanceForEach(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testMixedTryAdvanceForEachWithNull(String description, Collection exp, Supplier s) { + testMixedTryAdvanceForEach(exp, s, (Consumer b) -> b); + } +*/ + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitAfterFullTraversal(String description, Collection exp, Supplier s) { + testSplitAfterFullTraversal(s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitAfterFullTraversalWithNull(String description, Collection exp, Supplier s) { + testSplitAfterFullTraversal(s, (Consumer b) -> b); + } + + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitOnce(String description, Collection exp, Supplier s) { + testSplitOnce(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitOnceWithNull(String description, Collection exp, Supplier s) { + testSplitOnce(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitSixDeep(String description, Collection exp, Supplier s) { + testSplitSixDeep(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitSixDeepWithNull(String description, Collection exp, Supplier s) { + testSplitSixDeep(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitUntilNull(String description, Collection exp, Supplier s) { + testSplitUntilNull(exp, s, (Consumer b) -> b); + } + + @Test(dataProvider = "HashableIntSpliteratorWithNull") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testSplitUntilNullWithNull(String description, Collection exp, Supplier s) { + testSplitUntilNull(exp, s, (Consumer b) -> b); + } + + private static > void testForEach( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + S spliterator = supplier.get(); + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + ArrayList fromForEach = new ArrayList<>(); + spliterator = supplier.get(); + Consumer addToFromForEach = boxingAdapter.apply(fromForEach::add); + spliterator.forEachRemaining(addToFromForEach); + + // Assert that forEach now produces no elements + spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e))); + // Assert that tryAdvance now produce no elements + spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e))); + + // assert that size, tryAdvance, and forEach are consistent + if (sizeIfKnown >= 0) { + assertEquals(sizeIfKnown, exp.size()); + } + if (exp.contains(null)) { + assertTrue(fromForEach.contains(null)); + } + assertEquals(fromForEach.size(), exp.size()); + + assertContents(fromForEach, exp, isOrdered); + } + + private static > void testTryAdvance( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + S spliterator = supplier.get(); + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + spliterator = supplier.get(); + ArrayList fromTryAdvance = new ArrayList<>(); + Consumer addToFromTryAdvance = boxingAdapter.apply(fromTryAdvance::add); + while (spliterator.tryAdvance(addToFromTryAdvance)) { } + + // Assert that forEach now produces no elements + spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e))); + // Assert that tryAdvance now produce no elements + spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e))); + + // assert that size, tryAdvance, and forEach are consistent + if (sizeIfKnown >= 0) { + assertEquals(sizeIfKnown, exp.size()); + } + assertEquals(fromTryAdvance.size(), exp.size()); + + assertContents(fromTryAdvance, exp, isOrdered); + } + + private static > void testMixedTryAdvanceForEach( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + S spliterator = supplier.get(); + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + // tryAdvance first few elements, then forEach rest + ArrayList dest = new ArrayList<>(); + spliterator = supplier.get(); + Consumer addToDest = boxingAdapter.apply(dest::add); + for (int i = 0; i < 10 && spliterator.tryAdvance(addToDest); i++) { } + spliterator.forEachRemaining(addToDest); + + // Assert that forEach now produces no elements + spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e))); + // Assert that tryAdvance now produce no elements + spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e))); + + if (sizeIfKnown >= 0) { + assertEquals(sizeIfKnown, dest.size()); + } + assertEquals(dest.size(), exp.size()); + + if (isOrdered) { + assertEquals(dest, exp); + } + else { + assertContentsUnordered(dest, exp); + } + } + + private static > void testSplitAfterFullTraversal( + Supplier supplier, + UnaryOperator> boxingAdapter) { + // Full traversal using tryAdvance + Spliterator spliterator = supplier.get(); + while (spliterator.tryAdvance(boxingAdapter.apply(e -> { }))) { } + Spliterator split = spliterator.trySplit(); + assertNull(split); + + // Full traversal using forEach + spliterator = supplier.get(); + spliterator.forEachRemaining(boxingAdapter.apply(e -> { + })); + split = spliterator.trySplit(); + assertNull(split); + + // Full traversal using tryAdvance then forEach + spliterator = supplier.get(); + spliterator.tryAdvance(boxingAdapter.apply(e -> { })); + spliterator.forEachRemaining(boxingAdapter.apply(e -> { + })); + split = spliterator.trySplit(); + assertNull(split); + } + + private static > void testSplitOnce( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + S spliterator = supplier.get(); + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + ArrayList fromSplit = new ArrayList<>(); + Spliterator s1 = supplier.get(); + Spliterator s2 = s1.trySplit(); + long s1Size = s1.getExactSizeIfKnown(); + long s2Size = (s2 != null) ? s2.getExactSizeIfKnown() : 0; + + Consumer addToFromSplit = boxingAdapter.apply(fromSplit::add); + if (s2 != null) + s2.forEachRemaining(addToFromSplit); + s1.forEachRemaining(addToFromSplit); + + if (sizeIfKnown >= 0) { + assertEquals(sizeIfKnown, fromSplit.size()); + if (s1Size >= 0 && s2Size >= 0) + assertEquals(sizeIfKnown, s1Size + s2Size); + } + assertContents(fromSplit, exp, isOrdered); + } + + private static > void testSplitSixDeep( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + S spliterator = supplier.get(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + for (int depth=0; depth < 6; depth++) { + List dest = new ArrayList<>(); + spliterator = supplier.get(); + + assertSpliterator(spliterator); + + // verify splitting with forEach + visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), false); + assertContents(dest, exp, isOrdered); + + // verify splitting with tryAdvance + dest.clear(); + spliterator = supplier.get(); + visit(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), true); + assertContents(dest, exp, isOrdered); + } + } + + private static > void visit(int depth, int curLevel, + List dest, S spliterator, UnaryOperator> boxingAdapter, + int rootCharacteristics, boolean useTryAdvance) { + if (curLevel < depth) { + long beforeSize = spliterator.getExactSizeIfKnown(); + Spliterator split = spliterator.trySplit(); + if (split != null) { + assertSpliterator(split, rootCharacteristics); + assertSpliterator(spliterator, rootCharacteristics); + + if ((rootCharacteristics & Spliterator.SUBSIZED) != 0 && + (rootCharacteristics & Spliterator.SIZED) != 0) { + assertEquals(beforeSize, split.estimateSize() + spliterator.estimateSize()); + } + visit(depth, curLevel + 1, dest, split, boxingAdapter, rootCharacteristics, useTryAdvance); + } + visit(depth, curLevel + 1, dest, spliterator, boxingAdapter, rootCharacteristics, useTryAdvance); + } + else { + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + if (useTryAdvance) { + Consumer addToDest = boxingAdapter.apply(dest::add); + int count = 0; + while (spliterator.tryAdvance(addToDest)) { + ++count; + } + + if (sizeIfKnown >= 0) + assertEquals(sizeIfKnown, count); + + // Assert that forEach now produces no elements + spliterator.forEachRemaining(boxingAdapter.apply(e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e))); + + Spliterator split = spliterator.trySplit(); + assertNull(split); + } + else { + List leafDest = new ArrayList<>(); + Consumer addToLeafDest = boxingAdapter.apply(leafDest::add); + spliterator.forEachRemaining(addToLeafDest); + + if (sizeIfKnown >= 0) + assertEquals(sizeIfKnown, leafDest.size()); + + // Assert that forEach now produces no elements + spliterator.tryAdvance(boxingAdapter.apply(e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e))); + + Spliterator split = spliterator.trySplit(); + assertNull(split); + + dest.addAll(leafDest); + } + } + } + + private static > void testSplitUntilNull( + Collection exp, + Supplier supplier, + UnaryOperator> boxingAdapter) { + Spliterator s = supplier.get(); + boolean isOrdered = s.hasCharacteristics(Spliterator.ORDERED); + assertSpliterator(s); + + List splits = new ArrayList<>(); + Consumer c = boxingAdapter.apply(splits::add); + + testSplitUntilNull(new SplitNode(c, s)); + assertContents(splits, exp, isOrdered); + } + + private static class SplitNode { + // Constant for every node + final Consumer c; + final int rootCharacteristics; + + final Spliterator s; + + SplitNode(Consumer c, Spliterator s) { + this(c, s.characteristics(), s); + } + + private SplitNode(Consumer c, int rootCharacteristics, Spliterator s) { + this.c = c; + this.rootCharacteristics = rootCharacteristics; + this.s = s; + } + + SplitNode fromSplit(Spliterator split) { + return new SplitNode<>(c, rootCharacteristics, split); + } + } + + /** + * Set the maximum stack capacity to 0.25MB. This should be more than enough to detect a bad spliterator + * while not unduly disrupting test infrastructure given the test data sizes that are used are small. + * Note that j.u.c.ForkJoinPool sets the max queue size to 64M (1 << 26). + */ + private static final int MAXIMUM_STACK_CAPACITY = 1 << 18; // 0.25MB + + private static void testSplitUntilNull(SplitNode e) { + // Use an explicit stack to avoid a StackOverflowException when testing a Spliterator + // that when repeatedly split produces a right-balanced (and maybe degenerate) tree, or + // for a spliterator that is badly behaved. + Deque> stack = new ArrayDeque<>(); + stack.push(e); + + int iteration = 0; + while (!stack.isEmpty()) { + assertTrue(iteration++ < MAXIMUM_STACK_CAPACITY, "Exceeded maximum stack modification count of 1 << 18"); + + e = stack.pop(); + Spliterator parentAndRightSplit = e.s; + + long parentEstimateSize = parentAndRightSplit.estimateSize(); + assertTrue(parentEstimateSize >= 0, + String.format("Split size estimate %d < 0", parentEstimateSize)); + + long parentSize = parentAndRightSplit.getExactSizeIfKnown(); + Spliterator leftSplit = parentAndRightSplit.trySplit(); + if (leftSplit == null) { + parentAndRightSplit.forEachRemaining(e.c); + continue; + } + + assertSpliterator(leftSplit, e.rootCharacteristics); + assertSpliterator(parentAndRightSplit, e.rootCharacteristics); + + if (parentEstimateSize != Long.MAX_VALUE && leftSplit.estimateSize() > 0 && parentAndRightSplit.estimateSize() > 0) { + assertTrue(leftSplit.estimateSize() < parentEstimateSize, + String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize, + String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + } + else { + assertTrue(leftSplit.estimateSize() <= parentEstimateSize, + String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize, + String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + } + + long leftSize = leftSplit.getExactSizeIfKnown(); + long rightSize = parentAndRightSplit.getExactSizeIfKnown(); + if (parentSize >= 0 && leftSize >= 0 && rightSize >= 0) + assertEquals(parentSize, leftSize + rightSize, + String.format("exact left split size %d + exact right split size %d != parent exact split size %d", + leftSize, rightSize, parentSize)); + + // Add right side to stack first so left side is popped off first + stack.push(e.fromSplit(parentAndRightSplit)); + stack.push(e.fromSplit(leftSplit)); + } + } + + private static void assertSpliterator(Spliterator s, int rootCharacteristics) { + if ((rootCharacteristics & Spliterator.SUBSIZED) != 0) { + assertTrue(s.hasCharacteristics(Spliterator.SUBSIZED), + "Child split is not SUBSIZED when root split is SUBSIZED"); + } + assertSpliterator(s); + } + + private static void assertSpliterator(Spliterator s) { + if (s.hasCharacteristics(Spliterator.SUBSIZED)) { + assertTrue(s.hasCharacteristics(Spliterator.SIZED)); + } + if (s.hasCharacteristics(Spliterator.SIZED)) { + assertTrue(s.estimateSize() != Long.MAX_VALUE); + assertTrue(s.getExactSizeIfKnown() >= 0); + } + try { + s.getComparator(); + assertTrue(s.hasCharacteristics(Spliterator.SORTED)); + } catch (IllegalStateException e) { + assertFalse(s.hasCharacteristics(Spliterator.SORTED)); + } + } + + private static void assertContents(Collection actual, Collection expected, boolean isOrdered) { + if (isOrdered) { + assertEquals(actual, expected); + } + else { + assertContentsUnordered(actual, expected); + } + } + + private static void assertContentsUnordered(Iterable actual, Iterable expected) { + assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected)); + } + + private static Map toBoxedMultiset(Iterable c) { + Map result = new HashMap<>(); + c.forEach((Consumer) e -> { + if (result.containsKey((T)e)) { + result.put((T)e, new HashableInteger(((HashableInteger)result.get(e)).value + 1, 10)); + } else { + result.put((T)e, new HashableInteger(1, 10)); + } + }); + return result; + } + + private void executeAndCatch(Class expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + +}