/* * Copyright (C) 2008 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.Iterables.concat; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorKnownOrderRemoveSupportedMethod; import static java.util.Arrays.asList; import static java.util.Collections.nCopies; import static org.junit.contrib.truth.Truth.ASSERT; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.testing.CollectionTestSuiteBuilder; import com.google.common.collect.testing.TestStringCollectionGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.NullPointerTester; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * Tests for {@link Collections2}. * * @author Chris Povirk * @author Jared Levy */ @GwtCompatible(emulated = true) public class Collections2Test extends TestCase { @GwtIncompatible("suite") public static Test suite() { TestSuite suite = new TestSuite(Collections2Test.class.getSimpleName()); suite.addTest(testsForFilter()); suite.addTest(testsForFilterAll()); suite.addTest(testsForFilterLinkedList()); suite.addTest(testsForFilterNoNulls()); suite.addTest(testsForFilterFiltered()); suite.addTest(testsForTransform()); suite.addTestSuite(Collections2Test.class); return suite; } static final Predicate<String> NOT_YYY_ZZZ = new Predicate<String>() { @Override public boolean apply(String input) { return !"yyy".equals(input) && !"zzz".equals(input); } }; static final Predicate<String> LENGTH_1 = new Predicate<String>() { @Override public boolean apply(String input) { return input.length() == 1; } }; static final Predicate<String> STARTS_WITH_VOWEL = new Predicate<String>() { @Override public boolean apply(String input) { return asList('a', 'e', 'i', 'o', 'u').contains(input.charAt(0)); } }; @GwtIncompatible("suite") private static Test testsForFilter() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> unfiltered = newArrayList(); unfiltered.add("yyy"); unfiltered.addAll(asList(elements)); unfiltered.add("zzz"); return Collections2.filter(unfiltered, NOT_YYY_ZZZ); } }) .named("Collections2.filter") .withFeatures( CollectionFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite(); } @GwtIncompatible("suite") private static Test testsForFilterAll() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> unfiltered = newArrayList(); unfiltered.addAll(asList(elements)); return Collections2.filter(unfiltered, NOT_YYY_ZZZ); } }) .named("Collections2.filter") .withFeatures( CollectionFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite(); } @GwtIncompatible("suite") private static Test testsForFilterLinkedList() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> unfiltered = newLinkedList(); unfiltered.add("yyy"); unfiltered.addAll(asList(elements)); unfiltered.add("zzz"); return Collections2.filter(unfiltered, NOT_YYY_ZZZ); } }) .named("Collections2.filter") .withFeatures( CollectionFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite(); } @GwtIncompatible("suite") private static Test testsForFilterNoNulls() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> unfiltered = newArrayList(); unfiltered.add("yyy"); unfiltered.addAll(ImmutableList.copyOf(elements)); unfiltered.add("zzz"); return Collections2.filter(unfiltered, LENGTH_1); } }) .named("Collections2.filter, no nulls") .withFeatures( CollectionFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_QUERIES, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite(); } @GwtIncompatible("suite") private static Test testsForFilterFiltered() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> unfiltered = newArrayList(); unfiltered.add("yyy"); unfiltered.addAll(ImmutableList.copyOf(elements)); unfiltered.add("zzz"); unfiltered.add("abc"); return Collections2.filter( Collections2.filter(unfiltered, LENGTH_1), NOT_YYY_ZZZ); } }) .named("Collections2.filter, filtered input") .withFeatures( CollectionFeature.GENERAL_PURPOSE, CollectionFeature.KNOWN_ORDER, CollectionFeature.ALLOWS_NULL_QUERIES, CollectionSize.ANY) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite(); } public abstract static class FilterChangeTest extends TestCase { protected abstract <E> List<E> newList(); public void testFilterIllegalAdd() { List<String> unfiltered = newList(); Collection<String> filtered = Collections2.filter(unfiltered, NOT_YYY_ZZZ); filtered.add("a"); filtered.add("b"); ASSERT.that(filtered).hasContentsInOrder("a", "b"); try { filtered.add("yyy"); fail(); } catch (IllegalArgumentException expected) {} try { filtered.addAll(asList("c", "zzz", "d")); fail(); } catch (IllegalArgumentException expected) {} ASSERT.that(filtered).hasContentsInOrder("a", "b"); } public void testFilterChangeUnfiltered() { List<String> unfiltered = newList(); Collection<String> filtered = Collections2.filter(unfiltered, NOT_YYY_ZZZ); unfiltered.add("a"); unfiltered.add("yyy"); unfiltered.add("b"); ASSERT.that(unfiltered).hasContentsInOrder("a", "yyy", "b"); ASSERT.that(filtered).hasContentsInOrder("a", "b"); unfiltered.remove("a"); ASSERT.that(unfiltered).hasContentsInOrder("yyy", "b"); ASSERT.that(filtered).hasContentsInOrder("b"); unfiltered.clear(); ASSERT.that(unfiltered).isEmpty(); ASSERT.that(filtered).isEmpty(); unfiltered.add("yyy"); ASSERT.that(unfiltered).hasContentsInOrder("yyy"); ASSERT.that(filtered).isEmpty(); filtered.clear(); ASSERT.that(unfiltered).hasContentsInOrder("yyy"); ASSERT.that(filtered).isEmpty(); unfiltered.clear(); filtered.clear(); ASSERT.that(unfiltered).isEmpty(); ASSERT.that(filtered).isEmpty(); unfiltered.add("a"); ASSERT.that(unfiltered).hasContentsInOrder("a"); ASSERT.that(filtered).hasContentsInOrder("a"); filtered.clear(); ASSERT.that(unfiltered).isEmpty(); ASSERT.that(filtered).isEmpty(); unfiltered.clear(); Collections.addAll(unfiltered, "a", "b", "yyy", "zzz", "c", "d", "yyy", "zzz"); ASSERT.that(unfiltered).hasContentsInOrder( "a", "b", "yyy", "zzz", "c", "d", "yyy", "zzz"); ASSERT.that(filtered).hasContentsInOrder("a", "b", "c", "d"); filtered.clear(); ASSERT.that(unfiltered).hasContentsInOrder("yyy", "zzz", "yyy", "zzz"); ASSERT.that(filtered).isEmpty(); } public void testFilterChangeFiltered() { List<String> unfiltered = newList(); Collection<String> filtered = Collections2.filter(unfiltered, NOT_YYY_ZZZ); unfiltered.add("a"); unfiltered.add("yyy"); filtered.add("b"); ASSERT.that(unfiltered).hasContentsInOrder("a", "yyy", "b"); ASSERT.that(filtered).hasContentsInOrder("a", "b"); filtered.remove("a"); ASSERT.that(unfiltered).hasContentsInOrder("yyy", "b"); ASSERT.that(filtered).hasContentsInOrder("b"); filtered.clear(); ASSERT.that(unfiltered).hasContentsInOrder("yyy"); ASSERT.that(filtered); } public void testFilterFiltered() { List<String> unfiltered = newList(); Collection<String> filtered = Collections2.filter( Collections2.filter(unfiltered, LENGTH_1), STARTS_WITH_VOWEL); unfiltered.add("a"); unfiltered.add("b"); unfiltered.add("apple"); unfiltered.add("banana"); unfiltered.add("e"); ASSERT.that(filtered).hasContentsInOrder("a", "e"); ASSERT.that(unfiltered).hasContentsInOrder("a", "b", "apple", "banana", "e"); try { filtered.add("d"); fail(); } catch (IllegalArgumentException expected) {} try { filtered.add("egg"); fail(); } catch (IllegalArgumentException expected) {} ASSERT.that(filtered).hasContentsInOrder("a", "e"); ASSERT.that(unfiltered).hasContentsInOrder("a", "b", "apple", "banana", "e"); filtered.clear(); ASSERT.that(filtered).isEmpty(); ASSERT.that(unfiltered).hasContentsInOrder("b", "apple", "banana"); } } public static class ArrayListFilterChangeTest extends FilterChangeTest { @Override protected <E> List<E> newList() { return Lists.newArrayList(); } } public static class LinkedListFilterChangeTest extends FilterChangeTest { @Override protected <E> List<E> newList() { return Lists.newLinkedList(); } } private static final Function<String, String> REMOVE_FIRST_CHAR = new Function<String, String>() { @Override public String apply(String from) { return ((from == null) || "".equals(from)) ? null : from.substring(1); } }; @GwtIncompatible("suite") private static Test testsForTransform() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override public Collection<String> create(String[] elements) { List<String> list = newArrayList(); for (String element : elements) { list.add((element == null) ? null : "q" + element); } return Collections2.transform(list, REMOVE_FIRST_CHAR); } }) .named("Collections2.transform") .withFeatures( CollectionFeature.SUPPORTS_REMOVE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .createTestSuite(); } @GwtIncompatible("NullPointerTester") public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(Collections2.class); } public void testOrderedPermutationSetEmpty() { List<Integer> list = newArrayList(); Collection<List<Integer>> permutationSet = Collections2.orderedPermutations(list); assertEquals(1, permutationSet.size()); assertTrue(permutationSet.contains(list)); Iterator<List<Integer>> permutations = permutationSet.iterator(); assertNextPermutation(Lists.<Integer>newArrayList(), permutations); assertNoMorePermutations(permutations); } public void testOrderedPermutationSetOneElement() { List<Integer> list = newArrayList(1); Iterator<List<Integer>> permutations = Collections2.orderedPermutations(list).iterator(); assertNextPermutation(newArrayList(1), permutations); assertNoMorePermutations(permutations); } public void testOrderedPermutationSetThreeElements() { List<String> list = newArrayList("b", "a", "c"); Iterator<List<String>> permutations = Collections2.orderedPermutations(list).iterator(); assertNextPermutation(newArrayList("a", "b", "c"), permutations); assertNextPermutation(newArrayList("a", "c", "b"), permutations); assertNextPermutation(newArrayList("b", "a", "c"), permutations); assertNextPermutation(newArrayList("b", "c", "a"), permutations); assertNextPermutation(newArrayList("c", "a", "b"), permutations); assertNextPermutation(newArrayList("c", "b", "a"), permutations); assertNoMorePermutations(permutations); } public void testOrderedPermutationSetRepeatedElements() { List<Integer> list = newArrayList(1, 1, 2, 2); Iterator<List<Integer>> permutations = Collections2.orderedPermutations(list, Ordering.natural()).iterator(); assertNextPermutation(newArrayList(1, 1, 2, 2), permutations); assertNextPermutation(newArrayList(1, 2, 1, 2), permutations); assertNextPermutation(newArrayList(1, 2, 2, 1), permutations); assertNextPermutation(newArrayList(2, 1, 1, 2), permutations); assertNextPermutation(newArrayList(2, 1, 2, 1), permutations); assertNextPermutation(newArrayList(2, 2, 1, 1), permutations); assertNoMorePermutations(permutations); } public void testOrderedPermutationSetRepeatedElementsSize() { List<Integer> list = newArrayList(1, 1, 1, 1, 2, 2, 3); Collection<List<Integer>> permutations = Collections2.orderedPermutations(list, Ordering.natural()); assertPermutationsCount(105, permutations); } public void testOrderedPermutationSetSizeOverflow() { // 12 elements won't overflow assertEquals(479001600 /*12!*/, Collections2.orderedPermutations( newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)).size()); // 13 elements overflow an int assertEquals(Integer.MAX_VALUE, Collections2.orderedPermutations( newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)).size()); // 21 elements overflow a long assertEquals(Integer.MAX_VALUE, Collections2.orderedPermutations( newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)).size()); // Almost force an overflow in the binomial coefficient calculation assertEquals(1391975640 /*C(34,14)*/, Collections2.orderedPermutations( concat(nCopies(20, 1), nCopies(14, 2))).size()); // Do force an overflow in the binomial coefficient calculation assertEquals(Integer.MAX_VALUE, Collections2.orderedPermutations( concat(nCopies(21, 1), nCopies(14, 2))).size()); } public void testOrderedPermutationSetContains() { List<Integer> list = newArrayList(3, 2, 1); Collection<List<Integer>> permutationSet = Collections2.orderedPermutations(list); assertTrue(permutationSet.contains(newArrayList(1, 2, 3))); assertTrue(permutationSet.contains(newArrayList(2, 3, 1))); assertFalse(permutationSet.contains(newArrayList(1, 2))); assertFalse(permutationSet.contains(newArrayList(1, 1, 2, 3))); assertFalse(permutationSet.contains(newArrayList(1, 2, 3, 4))); assertFalse(permutationSet.contains(null)); } public void testPermutationSetEmpty() { Collection<List<Integer>> permutationSet = Collections2.permutations(Collections.<Integer>emptyList()); assertEquals(1, permutationSet.size()); assertTrue(permutationSet.contains(Collections.<Integer> emptyList())); Iterator<List<Integer>> permutations = permutationSet.iterator(); assertNextPermutation(Collections.<Integer> emptyList(), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetOneElement() { Iterator<List<Integer>> permutations = Collections2.permutations(Collections.<Integer> singletonList(1)) .iterator(); assertNextPermutation(newArrayList(1), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetTwoElements() { Iterator<List<Integer>> permutations = Collections2.permutations( newArrayList(1, 2)).iterator(); assertNextPermutation(newArrayList(1, 2), permutations); assertNextPermutation(newArrayList(2, 1), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetThreeElements() { Iterator<List<Integer>> permutations = Collections2.permutations( newArrayList(1, 2, 3)).iterator(); assertNextPermutation(newArrayList(1, 2, 3), permutations); assertNextPermutation(newArrayList(1, 3, 2), permutations); assertNextPermutation(newArrayList(3, 1, 2), permutations); assertNextPermutation(newArrayList(3, 2, 1), permutations); assertNextPermutation(newArrayList(2, 3, 1), permutations); assertNextPermutation(newArrayList(2, 1, 3), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetThreeElementsOutOfOrder() { Iterator<List<Integer>> permutations = Collections2.permutations( newArrayList(3, 2, 1)).iterator(); assertNextPermutation(newArrayList(3, 2, 1), permutations); assertNextPermutation(newArrayList(3, 1, 2), permutations); assertNextPermutation(newArrayList(1, 3, 2), permutations); assertNextPermutation(newArrayList(1, 2, 3), permutations); assertNextPermutation(newArrayList(2, 1, 3), permutations); assertNextPermutation(newArrayList(2, 3, 1), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetThreeRepeatedElements() { Iterator<List<Integer>> permutations = Collections2.permutations( newArrayList(1, 1, 2)).iterator(); assertNextPermutation(newArrayList(1, 1, 2), permutations); assertNextPermutation(newArrayList(1, 2, 1), permutations); assertNextPermutation(newArrayList(2, 1, 1), permutations); assertNextPermutation(newArrayList(2, 1, 1), permutations); assertNextPermutation(newArrayList(1, 2, 1), permutations); assertNextPermutation(newArrayList(1, 1, 2), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetFourElements() { Iterator<List<Integer>> permutations = Collections2.permutations( newArrayList(1, 2, 3, 4)).iterator(); assertNextPermutation(newArrayList(1, 2, 3, 4), permutations); assertNextPermutation(newArrayList(1, 2, 4, 3), permutations); assertNextPermutation(newArrayList(1, 4, 2, 3), permutations); assertNextPermutation(newArrayList(4, 1, 2, 3), permutations); assertNextPermutation(newArrayList(4, 1, 3, 2), permutations); assertNextPermutation(newArrayList(1, 4, 3, 2), permutations); assertNextPermutation(newArrayList(1, 3, 4, 2), permutations); assertNextPermutation(newArrayList(1, 3, 2, 4), permutations); assertNextPermutation(newArrayList(3, 1, 2, 4), permutations); assertNextPermutation(newArrayList(3, 1, 4, 2), permutations); assertNextPermutation(newArrayList(3, 4, 1, 2), permutations); assertNextPermutation(newArrayList(4, 3, 1, 2), permutations); assertNextPermutation(newArrayList(4, 3, 2, 1), permutations); assertNextPermutation(newArrayList(3, 4, 2, 1), permutations); assertNextPermutation(newArrayList(3, 2, 4, 1), permutations); assertNextPermutation(newArrayList(3, 2, 1, 4), permutations); assertNextPermutation(newArrayList(2, 3, 1, 4), permutations); assertNextPermutation(newArrayList(2, 3, 4, 1), permutations); assertNextPermutation(newArrayList(2, 4, 3, 1), permutations); assertNextPermutation(newArrayList(4, 2, 3, 1), permutations); assertNextPermutation(newArrayList(4, 2, 1, 3), permutations); assertNextPermutation(newArrayList(2, 4, 1, 3), permutations); assertNextPermutation(newArrayList(2, 1, 4, 3), permutations); assertNextPermutation(newArrayList(2, 1, 3, 4), permutations); assertNoMorePermutations(permutations); } public void testPermutationSetSize() { assertPermutationsCount(1, Collections2.permutations(Collections.<Integer>emptyList())); assertPermutationsCount(1, Collections2.permutations(newArrayList(1))); assertPermutationsCount(2, Collections2.permutations(newArrayList(1, 2))); assertPermutationsCount(6, Collections2.permutations(newArrayList(1, 2, 3))); assertPermutationsCount(5040, Collections2.permutations(newArrayList(1, 2, 3, 4, 5, 6, 7))); assertPermutationsCount(40320, Collections2.permutations(newArrayList(1, 2, 3, 4, 5, 6, 7, 8))); } public void testPermutationSetSizeOverflow() { // 13 elements overflow an int assertEquals(Integer.MAX_VALUE, Collections2.permutations(newArrayList( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)).size()); // 21 elements overflow a long assertEquals(Integer.MAX_VALUE, Collections2.orderedPermutations( newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)).size()); assertEquals(Integer.MAX_VALUE, Collections2.orderedPermutations( newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)).size()); } public void testPermutationSetContains() { List<Integer> list = newArrayList(3, 2, 1); Collection<List<Integer>> permutationSet = Collections2.permutations(list); assertTrue(permutationSet.contains(newArrayList(1, 2, 3))); assertTrue(permutationSet.contains(newArrayList(2, 3, 1))); assertFalse(permutationSet.contains(newArrayList(1, 2))); assertFalse(permutationSet.contains(newArrayList(1, 1, 2, 3))); assertFalse(permutationSet.contains(newArrayList(1, 2, 3, 4))); assertFalse(permutationSet.contains(null)); } private <T> void assertNextPermutation(List<T> expectedPermutation, Iterator<List<T>> permutations) { assertTrue("Expected another permutation, but there was none.", permutations.hasNext()); assertEquals(expectedPermutation, permutations.next()); } private <T> void assertNoMorePermutations( Iterator<List<T>> permutations) { assertFalse("Expected no more permutations, but there was one.", permutations.hasNext()); try { permutations.next(); fail("Expected NoSuchElementException."); } catch (NoSuchElementException expected) {} } private <T> void assertPermutationsCount(int expected, Collection<List<T>> permutationSet) { assertEquals(expected, permutationSet.size()); Iterator<List<T>> permutations = permutationSet.iterator(); for (int i = 0; i < expected; i++) { assertTrue(permutations.hasNext()); permutations.next(); } assertNoMorePermutations(permutations); } public void testToStringImplWithNullEntries() throws Exception { List<String> list = Lists.newArrayList(); list.add("foo"); list.add(null); assertEquals(list.toString(), Collections2.toStringImpl(list)); } }