/* * Copyright (C) 2007 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.base.Preconditions.checkArgument; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.DerivedComparable; import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.NavigableMapTestSuiteBuilder; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.TestSortedMapGenerator; import com.google.common.collect.testing.TestStringSetGenerator; import com.google.common.collect.testing.TestStringSortedSetGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.collect.testing.google.SortedSetMultimapTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; import com.google.common.testing.SerializableTester; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit tests for {@code TreeMultimap} with natural ordering. * * @author Jared Levy */ @GwtCompatible(emulated = true) public class TreeMultimapNaturalTest extends TestCase { @GwtIncompatible // suite public static Test suite() { TestSuite suite = new TestSuite(); // TODO(lowasser): should we force TreeMultimap to be more thorough about checking nulls? suite.addTest( SortedSetMultimapTestSuiteBuilder.using( new TestStringSetMultimapGenerator() { @Override protected SetMultimap create(Entry[] entries) { SetMultimap multimap = TreeMultimap.create( Ordering.natural().nullsFirst(), Ordering.natural().nullsFirst()); for (Entry entry : entries) { multimap.put(entry.getKey(), entry.getValue()); } return multimap; } @Override public Iterable> order( List> insertionOrder) { return new Ordering>() { @Override public int compare(Entry left, Entry right) { return ComparisonChain.start() .compare(left.getKey(), right.getKey(), Ordering.natural().nullsFirst()) .compare( left.getValue(), right.getValue(), Ordering.natural().nullsFirst()) .result(); } }.sortedCopy(insertionOrder); } }) .named("TreeMultimap nullsFirst") .withFeatures( MapFeature.ALLOWS_NULL_KEYS, MapFeature.ALLOWS_NULL_VALUES, MapFeature.ALLOWS_ANY_NULL_QUERIES, MapFeature.GENERAL_PURPOSE, MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, CollectionFeature.SUPPORTS_ITERATOR_REMOVE, CollectionFeature.KNOWN_ORDER, CollectionFeature.SERIALIZABLE, CollectionSize.ANY) .createTestSuite()); suite.addTest( NavigableSetTestSuiteBuilder.using( new TestStringSortedSetGenerator() { @Override protected NavigableSet create(String[] elements) { TreeMultimap multimap = TreeMultimap.create(Ordering.natural().nullsFirst(), Ordering.natural()); for (int i = 0; i < elements.length; i++) { multimap.put(elements[i], i); } return multimap.keySet(); } @Override public List order(List insertionOrder) { return Ordering.natural().nullsFirst().sortedCopy(insertionOrder); } }) .named("TreeMultimap.keySet") .withFeatures( CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .createTestSuite()); suite.addTest( NavigableMapTestSuiteBuilder.using( new TestSortedMapGenerator>() { @Override public String[] createKeyArray(int length) { return new String[length]; } @SuppressWarnings("unchecked") @Override public Collection[] createValueArray(int length) { return new Collection[length]; } @Override public SampleElements>> samples() { return new SampleElements<>( Helpers.mapEntry("a", (Collection) ImmutableSortedSet.of("alex")), Helpers.mapEntry( "b", (Collection) ImmutableSortedSet.of("bob", "bagel")), Helpers.mapEntry( "c", (Collection) ImmutableSortedSet.of("carl", "carol")), Helpers.mapEntry( "d", (Collection) ImmutableSortedSet.of("david", "dead")), Helpers.mapEntry( "e", (Collection) ImmutableSortedSet.of("eric", "elaine"))); } @SuppressWarnings("unchecked") @Override public Entry>[] createArray(int length) { return new Entry[length]; } @Override public Iterable>> order( List>> insertionOrder) { return new Ordering>() { @Override public int compare(Entry left, Entry right) { return left.getKey().compareTo(right.getKey()); } }.sortedCopy(insertionOrder); } @Override public NavigableMap> create(Object... elements) { TreeMultimap multimap = TreeMultimap.create(); for (Object o : elements) { @SuppressWarnings("unchecked") Entry> entry = (Entry>) o; checkArgument(!multimap.containsKey(entry.getKey())); multimap.putAll(entry.getKey(), entry.getValue()); } return multimap.asMap(); } @Override public Entry> belowSamplesLesser() { return Helpers.mapEntry( "-- a", (Collection) ImmutableSortedSet.of("--below")); } @Override public Entry> belowSamplesGreater() { return Helpers.mapEntry( "-- b", (Collection) ImmutableSortedSet.of("--below")); } @Override public Entry> aboveSamplesLesser() { return Helpers.mapEntry( "~~ b", (Collection) ImmutableSortedSet.of("~above")); } @Override public Entry> aboveSamplesGreater() { return Helpers.mapEntry( "~~ c", (Collection) ImmutableSortedSet.of("~above")); } }) .named("TreeMultimap.asMap") .withFeatures( MapFeature.SUPPORTS_REMOVE, MapFeature.REJECTS_DUPLICATES_AT_CREATION, CollectionFeature.SUPPORTS_ITERATOR_REMOVE, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .createTestSuite()); suite.addTest( NavigableSetTestSuiteBuilder.using( new TestStringSetGenerator() { @Override protected Set create(String[] elements) { TreeMultimap multimap = TreeMultimap.create(Ordering.natural(), Ordering.natural().nullsFirst()); multimap.putAll(1, Arrays.asList(elements)); return multimap.get(1); } @Override public List order(List insertionOrder) { return Ordering.natural().nullsFirst().sortedCopy(insertionOrder); } }) .named("TreeMultimap.get") .withFeatures( CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.GENERAL_PURPOSE, CollectionFeature.KNOWN_ORDER, CollectionSize.ANY) .createTestSuite()); suite.addTest( NavigableSetTestSuiteBuilder.using( new TestStringSetGenerator() { @Override protected Set create(String[] elements) { TreeMultimap multimap = TreeMultimap.create(Ordering.natural(), Ordering.natural().nullsFirst()); multimap.putAll(1, Arrays.asList(elements)); return (Set) multimap.asMap().entrySet().iterator().next().getValue(); } @Override public List order(List insertionOrder) { return Ordering.natural().nullsFirst().sortedCopy(insertionOrder); } }) .named("TreeMultimap.asMap.entrySet collection") .withFeatures( CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.GENERAL_PURPOSE, CollectionFeature.KNOWN_ORDER, CollectionSize.ONE, CollectionSize.SEVERAL) .createTestSuite()); suite.addTestSuite(TreeMultimapNaturalTest.class); return suite; } protected SetMultimap create() { return TreeMultimap.create(); } /** Create and populate a {@code TreeMultimap} with the natural ordering of keys and values. */ private TreeMultimap createPopulate() { TreeMultimap multimap = TreeMultimap.create(); multimap.put("google", 2); multimap.put("google", 6); multimap.put("foo", 3); multimap.put("foo", 1); multimap.put("foo", 7); multimap.put("tree", 4); multimap.put("tree", 0); return multimap; } public void testToString() { SetMultimap multimap = create(); multimap.putAll("bar", Arrays.asList(3, 1, 2)); multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4)); assertEquals("{bar=[1, 2, 3], foo=[-1, 1, 2, 3, 4]}", multimap.toString()); } public void testOrderedGet() { TreeMultimap multimap = createPopulate(); assertThat(multimap.get("foo")).containsExactly(1, 3, 7).inOrder(); assertThat(multimap.get("google")).containsExactly(2, 6).inOrder(); assertThat(multimap.get("tree")).containsExactly(0, 4).inOrder(); } public void testOrderedKeySet() { TreeMultimap multimap = createPopulate(); assertThat(multimap.keySet()).containsExactly("foo", "google", "tree").inOrder(); } public void testOrderedAsMapEntries() { TreeMultimap multimap = createPopulate(); Iterator>> iterator = multimap.asMap().entrySet().iterator(); Entry> entry = iterator.next(); assertEquals("foo", entry.getKey()); assertThat(entry.getValue()).containsExactly(1, 3, 7); entry = iterator.next(); assertEquals("google", entry.getKey()); assertThat(entry.getValue()).containsExactly(2, 6); entry = iterator.next(); assertEquals("tree", entry.getKey()); assertThat(entry.getValue()).containsExactly(0, 4); } public void testOrderedEntries() { TreeMultimap multimap = createPopulate(); assertThat(multimap.entries()) .containsExactly( Maps.immutableEntry("foo", 1), Maps.immutableEntry("foo", 3), Maps.immutableEntry("foo", 7), Maps.immutableEntry("google", 2), Maps.immutableEntry("google", 6), Maps.immutableEntry("tree", 0), Maps.immutableEntry("tree", 4)) .inOrder(); } public void testOrderedValues() { TreeMultimap multimap = createPopulate(); assertThat(multimap.values()).containsExactly(1, 3, 7, 2, 6, 0, 4).inOrder(); } public void testMultimapConstructor() { SetMultimap multimap = create(); multimap.putAll("bar", Arrays.asList(3, 1, 2)); multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4)); TreeMultimap copy = TreeMultimap.create(multimap); assertEquals(multimap, copy); } private static final Comparator KEY_COMPARATOR = Ordering.natural(); private static final Comparator VALUE_COMPARATOR = Ordering.natural().reverse().nullsFirst(); /** * Test that creating one TreeMultimap from another does not copy the comparators from the source * TreeMultimap. */ public void testCreateFromTreeMultimap() { Multimap tree = TreeMultimap.create(KEY_COMPARATOR, VALUE_COMPARATOR); tree.put(1.0, 2.0); tree.put(2.0, 3.0); tree.put(3.0, 4.0); tree.put(4.0, 5.0); TreeMultimap copyFromTree = TreeMultimap.create(tree); assertEquals(tree, copyFromTree); assertSame(Ordering.natural(), copyFromTree.keyComparator()); assertSame(Ordering.natural(), copyFromTree.valueComparator()); assertSame(Ordering.natural(), copyFromTree.get(1.0).comparator()); } /** Test that creating one TreeMultimap from a non-TreeMultimap results in natural ordering. */ public void testCreateFromHashMultimap() { Multimap hash = HashMultimap.create(); hash.put(1.0, 2.0); hash.put(2.0, 3.0); hash.put(3.0, 4.0); hash.put(4.0, 5.0); TreeMultimap copyFromHash = TreeMultimap.create(hash); assertEquals(hash, copyFromHash); assertEquals(Ordering.natural(), copyFromHash.keyComparator()); assertEquals(Ordering.natural(), copyFromHash.valueComparator()); } /** Test that creating one TreeMultimap from a SortedSetMultimap uses natural ordering. */ public void testCreateFromSortedSetMultimap() { SortedSetMultimap tree = TreeMultimap.create(KEY_COMPARATOR, VALUE_COMPARATOR); tree.put(1.0, 2.0); tree.put(2.0, 3.0); tree.put(3.0, 4.0); tree.put(4.0, 5.0); SortedSetMultimap sorted = Multimaps.unmodifiableSortedSetMultimap(tree); TreeMultimap copyFromSorted = TreeMultimap.create(sorted); assertEquals(tree, copyFromSorted); assertSame(Ordering.natural(), copyFromSorted.keyComparator()); assertSame(Ordering.natural(), copyFromSorted.valueComparator()); assertSame(Ordering.natural(), copyFromSorted.get(1.0).comparator()); } public void testComparators() { TreeMultimap multimap = TreeMultimap.create(); assertEquals(Ordering.natural(), multimap.keyComparator()); assertEquals(Ordering.natural(), multimap.valueComparator()); } @GwtIncompatible // SerializableTester public void testExplicitComparatorSerialization() { TreeMultimap multimap = createPopulate(); TreeMultimap copy = SerializableTester.reserializeAndAssert(multimap); assertThat(copy.values()).containsExactly(1, 3, 7, 2, 6, 0, 4).inOrder(); assertThat(copy.keySet()).containsExactly("foo", "google", "tree").inOrder(); assertEquals(multimap.keyComparator(), copy.keyComparator()); assertEquals(multimap.valueComparator(), copy.valueComparator()); } @GwtIncompatible // SerializableTester public void testTreeMultimapDerived() { TreeMultimap multimap = TreeMultimap.create(); assertEquals(ImmutableMultimap.of(), multimap); multimap.put(new DerivedComparable("foo"), new DerivedComparable("f")); multimap.put(new DerivedComparable("foo"), new DerivedComparable("o")); multimap.put(new DerivedComparable("foo"), new DerivedComparable("o")); multimap.put(new DerivedComparable("bar"), new DerivedComparable("b")); multimap.put(new DerivedComparable("bar"), new DerivedComparable("a")); multimap.put(new DerivedComparable("bar"), new DerivedComparable("r")); assertThat(multimap.keySet()) .containsExactly(new DerivedComparable("bar"), new DerivedComparable("foo")) .inOrder(); assertThat(multimap.values()) .containsExactly( new DerivedComparable("a"), new DerivedComparable("b"), new DerivedComparable("r"), new DerivedComparable("f"), new DerivedComparable("o")) .inOrder(); assertEquals(Ordering.natural(), multimap.keyComparator()); assertEquals(Ordering.natural(), multimap.valueComparator()); SerializableTester.reserializeAndAssert(multimap); } @GwtIncompatible // SerializableTester public void testTreeMultimapNonGeneric() { TreeMultimap multimap = TreeMultimap.create(); assertEquals(ImmutableMultimap.of(), multimap); multimap.put(new LegacyComparable("foo"), new LegacyComparable("f")); multimap.put(new LegacyComparable("foo"), new LegacyComparable("o")); multimap.put(new LegacyComparable("foo"), new LegacyComparable("o")); multimap.put(new LegacyComparable("bar"), new LegacyComparable("b")); multimap.put(new LegacyComparable("bar"), new LegacyComparable("a")); multimap.put(new LegacyComparable("bar"), new LegacyComparable("r")); assertThat(multimap.keySet()) .containsExactly(new LegacyComparable("bar"), new LegacyComparable("foo")) .inOrder(); assertThat(multimap.values()) .containsExactly( new LegacyComparable("a"), new LegacyComparable("b"), new LegacyComparable("r"), new LegacyComparable("f"), new LegacyComparable("o")) .inOrder(); assertEquals(Ordering.natural(), multimap.keyComparator()); assertEquals(Ordering.natural(), multimap.valueComparator()); SerializableTester.reserializeAndAssert(multimap); } public void testTreeMultimapAsMapSorted() { TreeMultimap multimap = createPopulate(); SortedMap> asMap = multimap.asMap(); assertEquals(Ordering.natural(), asMap.comparator()); assertEquals("foo", asMap.firstKey()); assertEquals("tree", asMap.lastKey()); Set fooValues = ImmutableSet.of(1, 3, 7); Set googleValues = ImmutableSet.of(2, 6); Set treeValues = ImmutableSet.of(4, 0); assertEquals(ImmutableMap.of("google", googleValues, "tree", treeValues), asMap.tailMap("g")); assertEquals(ImmutableMap.of("google", googleValues, "foo", fooValues), asMap.headMap("h")); assertEquals(ImmutableMap.of("google", googleValues), asMap.subMap("g", "h")); } public void testTailSetClear() { TreeMultimap multimap = TreeMultimap.create(); multimap.put("a", 1); multimap.put("a", 11); multimap.put("b", 2); multimap.put("c", 3); multimap.put("d", 4); multimap.put("e", 5); multimap.put("e", 55); multimap.keySet().tailSet("d").clear(); assertEquals(ImmutableSet.of("a", "b", "c"), multimap.keySet()); assertEquals(4, multimap.size()); assertEquals(4, multimap.values().size()); assertEquals(4, multimap.keys().size()); } @GwtIncompatible // reflection public void testKeySetBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { if (m.getName().equals("keySet") && m.getReturnType().equals(SortedSet.class)) { return; } } fail("No bridge method found"); } @GwtIncompatible // reflection public void testAsMapBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { if (m.getName().equals("asMap") && m.getReturnType().equals(SortedMap.class)) { return; } } } @GwtIncompatible // reflection public void testGetBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { if (m.getName().equals("get") && m.getReturnType().equals(SortedSet.class)) { return; } } fail("No bridge method found"); } }