• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.annotations.GwtIncompatible;
24 import com.google.common.collect.testing.DerivedComparable;
25 import com.google.common.collect.testing.Helpers;
26 import com.google.common.collect.testing.NavigableMapTestSuiteBuilder;
27 import com.google.common.collect.testing.NavigableSetTestSuiteBuilder;
28 import com.google.common.collect.testing.SampleElements;
29 import com.google.common.collect.testing.TestSortedMapGenerator;
30 import com.google.common.collect.testing.TestStringSetGenerator;
31 import com.google.common.collect.testing.TestStringSortedSetGenerator;
32 import com.google.common.collect.testing.features.CollectionFeature;
33 import com.google.common.collect.testing.features.CollectionSize;
34 import com.google.common.collect.testing.features.MapFeature;
35 import com.google.common.collect.testing.google.SortedSetMultimapTestSuiteBuilder;
36 import com.google.common.collect.testing.google.TestStringSetMultimapGenerator;
37 import com.google.common.testing.SerializableTester;
38 
39 import junit.framework.Test;
40 import junit.framework.TestCase;
41 import junit.framework.TestSuite;
42 
43 import java.lang.reflect.Method;
44 import java.util.Arrays;
45 import java.util.Collection;
46 import java.util.Comparator;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Map.Entry;
51 import java.util.NavigableMap;
52 import java.util.NavigableSet;
53 import java.util.Set;
54 import java.util.SortedMap;
55 import java.util.SortedSet;
56 
57 /**
58  * Unit tests for {@code TreeMultimap} with natural ordering.
59  *
60  * @author Jared Levy
61  */
62 @GwtCompatible(emulated = true)
63 public class TreeMultimapNaturalTest extends TestCase {
64 
65   @GwtIncompatible("suite")
suite()66   public static Test suite() {
67     TestSuite suite = new TestSuite();
68     // TODO(user): should we force TreeMultimap to be more thorough about checking nulls?
69     suite.addTest(SortedSetMultimapTestSuiteBuilder.using(new TestStringSetMultimapGenerator() {
70         @Override
71         protected SetMultimap<String, String> create(Entry<String, String>[] entries) {
72           SetMultimap<String, String> multimap = TreeMultimap.create(
73               Ordering.natural().nullsFirst(), Ordering.natural().nullsFirst());
74           for (Entry<String, String> entry : entries) {
75             multimap.put(entry.getKey(), entry.getValue());
76           }
77           return multimap;
78         }
79 
80         @Override
81         public Iterable<Entry<String, String>> order(List<Entry<String, String>> insertionOrder) {
82           return new Ordering<Entry<String, String>>() {
83             @Override
84             public int compare(Entry<String, String> left, Entry<String, String> right) {
85               return ComparisonChain.start()
86                   .compare(left.getKey(), right.getKey(), Ordering.natural().nullsFirst())
87                   .compare(left.getValue(), right.getValue(), Ordering.natural().nullsFirst())
88                   .result();
89             }
90           }.sortedCopy(insertionOrder);
91         }
92       })
93       .named("TreeMultimap nullsFirst")
94       .withFeatures(
95           MapFeature.ALLOWS_NULL_KEYS,
96           MapFeature.ALLOWS_NULL_VALUES,
97           MapFeature.ALLOWS_ANY_NULL_QUERIES,
98           MapFeature.GENERAL_PURPOSE,
99           MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
100           CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
101           CollectionFeature.KNOWN_ORDER,
102           CollectionFeature.SERIALIZABLE,
103           CollectionSize.ANY)
104       .createTestSuite());
105     suite.addTest(NavigableSetTestSuiteBuilder.using(new TestStringSortedSetGenerator() {
106         @Override
107         protected NavigableSet<String> create(String[] elements) {
108           TreeMultimap<String, Integer> multimap = TreeMultimap.create(
109               Ordering.natural().nullsFirst(), Ordering.natural());
110           for (int i = 0; i < elements.length; i++) {
111             multimap.put(elements[i], i);
112           }
113           return multimap.keySet();
114         }
115 
116         @Override
117         public List<String> order(List<String> insertionOrder) {
118           return Ordering.natural().nullsFirst().sortedCopy(insertionOrder);
119         }
120       })
121       .named("TreeMultimap.keySet")
122       .withFeatures(
123           CollectionFeature.ALLOWS_NULL_VALUES,
124           CollectionFeature.REMOVE_OPERATIONS,
125           CollectionFeature.KNOWN_ORDER,
126           CollectionSize.ANY)
127       .createTestSuite());
128     suite.addTest(NavigableMapTestSuiteBuilder.using(
129       new TestSortedMapGenerator<String, Collection<String>>() {
130 
131         @Override
132         public String[] createKeyArray(int length) {
133           return new String[length];
134         }
135 
136         @SuppressWarnings("unchecked")
137         @Override
138         public Collection<String>[] createValueArray(int length) {
139           return new Collection[length];
140         }
141 
142         @Override
143         public SampleElements<Entry<String, Collection<String>>> samples() {
144           return new SampleElements<Entry<String, Collection<String>>>(
145               Helpers.mapEntry("a", (Collection<String>) ImmutableSortedSet.of("alex")),
146               Helpers.mapEntry("b", (Collection<String>) ImmutableSortedSet.of("bob", "bagel")),
147               Helpers.mapEntry("c", (Collection<String>) ImmutableSortedSet.of("carl", "carol")),
148               Helpers.mapEntry("d", (Collection<String>) ImmutableSortedSet.of("david", "dead")),
149               Helpers.mapEntry("e", (Collection<String>) ImmutableSortedSet.of("eric", "elaine")));
150         }
151 
152         @SuppressWarnings("unchecked")
153         @Override
154         public Entry<String, Collection<String>>[] createArray(int length) {
155           return new Entry[length];
156         }
157 
158         @Override
159         public Iterable<Entry<String, Collection<String>>> order(
160             List<Entry<String, Collection<String>>> insertionOrder) {
161           return new Ordering<Entry<String, ?>>() {
162             @Override
163             public int compare(Entry<String, ?> left, Entry<String, ?> right) {
164               return left.getKey().compareTo(right.getKey());
165             }
166           }.sortedCopy(insertionOrder);
167         }
168 
169         @Override
170         public NavigableMap<String, Collection<String>> create(Object... elements) {
171           TreeMultimap<String, String> multimap = TreeMultimap.create();
172           for (Object o : elements) {
173             @SuppressWarnings("unchecked")
174             Entry<String, Collection<String>> entry = (Entry<String, Collection<String>>) o;
175             checkArgument(!multimap.containsKey(entry.getKey()));
176             multimap.putAll(entry.getKey(), entry.getValue());
177           }
178           return multimap.asMap();
179         }
180 
181         @Override
182         public Entry<String, Collection<String>> belowSamplesLesser() {
183           return Helpers.mapEntry("-- a", (Collection<String>) ImmutableSortedSet.of("--below"));
184         }
185 
186         @Override
187         public Entry<String, Collection<String>> belowSamplesGreater() {
188           return Helpers.mapEntry("-- b", (Collection<String>) ImmutableSortedSet.of("--below"));
189         }
190 
191         @Override
192         public Entry<String, Collection<String>> aboveSamplesLesser() {
193           return Helpers.mapEntry("~~ b", (Collection<String>) ImmutableSortedSet.of("~above"));
194         }
195 
196         @Override
197         public Entry<String, Collection<String>> aboveSamplesGreater() {
198           return Helpers.mapEntry("~~ c", (Collection<String>) ImmutableSortedSet.of("~above"));
199         }
200       })
201       .named("TreeMultimap.asMap")
202       .withFeatures(
203           MapFeature.SUPPORTS_REMOVE,
204           MapFeature.REJECTS_DUPLICATES_AT_CREATION,
205           CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
206           CollectionFeature.KNOWN_ORDER,
207           CollectionSize.ANY)
208       .createTestSuite());
209     suite.addTest(NavigableSetTestSuiteBuilder.using(new TestStringSetGenerator() {
210         @Override
211         protected Set<String> create(String[] elements) {
212           TreeMultimap<Integer, String> multimap = TreeMultimap.create(
213               Ordering.natural(), Ordering.natural().nullsFirst());
214           multimap.putAll(1, Arrays.asList(elements));
215           return multimap.get(1);
216         }
217 
218         @Override
219         public List<String> order(List<String> insertionOrder) {
220           return Ordering.natural().nullsFirst().sortedCopy(insertionOrder);
221         }
222       })
223       .named("TreeMultimap.get")
224       .withFeatures(
225           CollectionFeature.ALLOWS_NULL_VALUES,
226           CollectionFeature.GENERAL_PURPOSE,
227           CollectionFeature.KNOWN_ORDER,
228           CollectionSize.ANY)
229       .createTestSuite());
230     suite.addTest(NavigableSetTestSuiteBuilder.using(new TestStringSetGenerator() {
231         @Override
232         protected Set<String> create(String[] elements) {
233           TreeMultimap<Integer, String> multimap = TreeMultimap.create(
234               Ordering.natural(), Ordering.natural().nullsFirst());
235           multimap.putAll(1, Arrays.asList(elements));
236           return (Set<String>) multimap.asMap().entrySet().iterator().next().getValue();
237         }
238 
239         @Override
240         public List<String> order(List<String> insertionOrder) {
241           return Ordering.natural().nullsFirst().sortedCopy(insertionOrder);
242         }
243       })
244       .named("TreeMultimap.asMap.entrySet collection")
245       .withFeatures(
246           CollectionFeature.ALLOWS_NULL_VALUES,
247           CollectionFeature.GENERAL_PURPOSE,
248           CollectionFeature.KNOWN_ORDER,
249           CollectionSize.ONE,
250           CollectionSize.SEVERAL)
251       .createTestSuite());
252     suite.addTestSuite(TreeMultimapNaturalTest.class);
253     return suite;
254   }
255 
create()256   protected SetMultimap<String, Integer> create() {
257     return TreeMultimap.create();
258   }
259 
260   /**
261    * Create and populate a {@code TreeMultimap} with the natural ordering of
262    * keys and values.
263    */
createPopulate()264   private TreeMultimap<String, Integer> createPopulate() {
265     TreeMultimap<String, Integer> multimap = TreeMultimap.create();
266     multimap.put("google", 2);
267     multimap.put("google", 6);
268     multimap.put("foo", 3);
269     multimap.put("foo", 1);
270     multimap.put("foo", 7);
271     multimap.put("tree", 4);
272     multimap.put("tree", 0);
273     return multimap;
274   }
275 
testToString()276   public void testToString() {
277     SetMultimap<String, Integer> multimap = create();
278     multimap.putAll("bar", Arrays.asList(3, 1, 2));
279     multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4));
280     assertEquals("{bar=[1, 2, 3], foo=[-1, 1, 2, 3, 4]}",
281         multimap.toString());
282   }
283 
testOrderedGet()284   public void testOrderedGet() {
285     TreeMultimap<String, Integer> multimap = createPopulate();
286     assertThat(multimap.get("foo")).has().exactly(1, 3, 7).inOrder();
287     assertThat(multimap.get("google")).has().exactly(2, 6).inOrder();
288     assertThat(multimap.get("tree")).has().exactly(0, 4).inOrder();
289   }
290 
testOrderedKeySet()291   public void testOrderedKeySet() {
292     TreeMultimap<String, Integer> multimap = createPopulate();
293     assertThat(multimap.keySet()).has().exactly("foo", "google", "tree").inOrder();
294   }
295 
testOrderedAsMapEntries()296   public void testOrderedAsMapEntries() {
297     TreeMultimap<String, Integer> multimap = createPopulate();
298     Iterator<Map.Entry<String, Collection<Integer>>> iterator =
299         multimap.asMap().entrySet().iterator();
300     Map.Entry<String, Collection<Integer>> entry = iterator.next();
301     assertEquals("foo", entry.getKey());
302     assertThat(entry.getValue()).has().exactly(1, 3, 7);
303     entry = iterator.next();
304     assertEquals("google", entry.getKey());
305     assertThat(entry.getValue()).has().exactly(2, 6);
306     entry = iterator.next();
307     assertEquals("tree", entry.getKey());
308     assertThat(entry.getValue()).has().exactly(0, 4);
309   }
310 
testOrderedEntries()311   public void testOrderedEntries() {
312     TreeMultimap<String, Integer> multimap = createPopulate();
313     assertThat(multimap.entries()).has().exactly(
314         Maps.immutableEntry("foo", 1),
315         Maps.immutableEntry("foo", 3),
316         Maps.immutableEntry("foo", 7),
317         Maps.immutableEntry("google", 2),
318         Maps.immutableEntry("google", 6),
319         Maps.immutableEntry("tree", 0),
320         Maps.immutableEntry("tree", 4)).inOrder();
321   }
322 
testOrderedValues()323   public void testOrderedValues() {
324     TreeMultimap<String, Integer> multimap = createPopulate();
325     assertThat(multimap.values()).has().exactly(
326         1, 3, 7, 2, 6, 0, 4).inOrder();
327   }
328 
testMultimapConstructor()329   public void testMultimapConstructor() {
330     SetMultimap<String, Integer> multimap = create();
331     multimap.putAll("bar", Arrays.asList(3, 1, 2));
332     multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4));
333     TreeMultimap<String, Integer> copy = TreeMultimap.create(multimap);
334     assertEquals(multimap, copy);
335   }
336 
337   private static final Comparator<Double> KEY_COMPARATOR =
338       Ordering.natural();
339 
340   private static final Comparator<Double> VALUE_COMPARATOR =
341       Ordering.natural().reverse().nullsFirst();
342 
343   /**
344    * Test that creating one TreeMultimap from another does not copy the
345    * comparators from the source TreeMultimap.
346    */
testCreateFromTreeMultimap()347   public void testCreateFromTreeMultimap() {
348     Multimap<Double, Double> tree = TreeMultimap.create(KEY_COMPARATOR, VALUE_COMPARATOR);
349     tree.put(1.0, 2.0);
350     tree.put(2.0, 3.0);
351     tree.put(3.0, 4.0);
352     tree.put(4.0, 5.0);
353 
354     TreeMultimap<Double, Double> copyFromTree = TreeMultimap.create(tree);
355     assertEquals(tree, copyFromTree);
356     assertSame(Ordering.natural(), copyFromTree.keyComparator());
357     assertSame(Ordering.natural(), copyFromTree.valueComparator());
358     assertSame(Ordering.natural(), copyFromTree.get(1.0).comparator());
359   }
360 
361   /**
362    * Test that creating one TreeMultimap from a non-TreeMultimap
363    * results in natural ordering.
364    */
testCreateFromHashMultimap()365   public void testCreateFromHashMultimap() {
366     Multimap<Double, Double> hash = HashMultimap.create();
367     hash.put(1.0, 2.0);
368     hash.put(2.0, 3.0);
369     hash.put(3.0, 4.0);
370     hash.put(4.0, 5.0);
371 
372     TreeMultimap<Double, Double> copyFromHash = TreeMultimap.create(hash);
373     assertEquals(hash, copyFromHash);
374     assertEquals(Ordering.natural(), copyFromHash.keyComparator());
375     assertEquals(Ordering.natural(), copyFromHash.valueComparator());
376   }
377 
378   /**
379    * Test that creating one TreeMultimap from a SortedSetMultimap uses natural
380    * ordering.
381    */
testCreateFromSortedSetMultimap()382   public void testCreateFromSortedSetMultimap() {
383     SortedSetMultimap<Double, Double> tree = TreeMultimap.create(KEY_COMPARATOR, VALUE_COMPARATOR);
384     tree.put(1.0, 2.0);
385     tree.put(2.0, 3.0);
386     tree.put(3.0, 4.0);
387     tree.put(4.0, 5.0);
388 
389     SortedSetMultimap<Double, Double> sorted = Multimaps.unmodifiableSortedSetMultimap(tree);
390     TreeMultimap<Double, Double> copyFromSorted = TreeMultimap.create(sorted);
391     assertEquals(tree, copyFromSorted);
392     assertSame(Ordering.natural(), copyFromSorted.keyComparator());
393     assertSame(Ordering.natural(), copyFromSorted.valueComparator());
394     assertSame(Ordering.natural(), copyFromSorted.get(1.0).comparator());
395   }
396 
testComparators()397   public void testComparators() {
398     TreeMultimap<String, Integer> multimap = TreeMultimap.create();
399     assertEquals(Ordering.natural(), multimap.keyComparator());
400     assertEquals(Ordering.natural(), multimap.valueComparator());
401   }
402 
403   @GwtIncompatible("SerializableTester")
testExplicitComparatorSerialization()404   public void testExplicitComparatorSerialization() {
405     TreeMultimap<String, Integer> multimap = createPopulate();
406     TreeMultimap<String, Integer> copy
407         = SerializableTester.reserializeAndAssert(multimap);
408     assertThat(copy.values()).has().exactly(1, 3, 7, 2, 6, 0, 4).inOrder();
409     assertThat(copy.keySet()).has().exactly("foo", "google", "tree").inOrder();
410     assertEquals(multimap.keyComparator(), copy.keyComparator());
411     assertEquals(multimap.valueComparator(), copy.valueComparator());
412   }
413 
414   @GwtIncompatible("SerializableTester")
testTreeMultimapDerived()415   public void testTreeMultimapDerived() {
416     TreeMultimap<DerivedComparable, DerivedComparable> multimap = TreeMultimap.create();
417     assertEquals(ImmutableMultimap.of(), multimap);
418     multimap.put(new DerivedComparable("foo"), new DerivedComparable("f"));
419     multimap.put(new DerivedComparable("foo"), new DerivedComparable("o"));
420     multimap.put(new DerivedComparable("foo"), new DerivedComparable("o"));
421     multimap.put(new DerivedComparable("bar"), new DerivedComparable("b"));
422     multimap.put(new DerivedComparable("bar"), new DerivedComparable("a"));
423     multimap.put(new DerivedComparable("bar"), new DerivedComparable("r"));
424     assertThat(multimap.keySet()).has().exactly(
425         new DerivedComparable("bar"), new DerivedComparable("foo")).inOrder();
426     assertThat(multimap.values()).has().exactly(
427         new DerivedComparable("a"), new DerivedComparable("b"), new DerivedComparable("r"),
428         new DerivedComparable("f"), new DerivedComparable("o")).inOrder();
429     assertEquals(Ordering.natural(), multimap.keyComparator());
430     assertEquals(Ordering.natural(), multimap.valueComparator());
431     SerializableTester.reserializeAndAssert(multimap);
432   }
433 
434   @GwtIncompatible("SerializableTester")
testTreeMultimapNonGeneric()435   public void testTreeMultimapNonGeneric() {
436     TreeMultimap<LegacyComparable, LegacyComparable> multimap
437         = TreeMultimap.create();
438     assertEquals(ImmutableMultimap.of(), multimap);
439     multimap.put(new LegacyComparable("foo"), new LegacyComparable("f"));
440     multimap.put(new LegacyComparable("foo"), new LegacyComparable("o"));
441     multimap.put(new LegacyComparable("foo"), new LegacyComparable("o"));
442     multimap.put(new LegacyComparable("bar"), new LegacyComparable("b"));
443     multimap.put(new LegacyComparable("bar"), new LegacyComparable("a"));
444     multimap.put(new LegacyComparable("bar"), new LegacyComparable("r"));
445     assertThat(multimap.keySet()).has().exactly(
446         new LegacyComparable("bar"), new LegacyComparable("foo")).inOrder();
447     assertThat(multimap.values()).has().exactly(
448         new LegacyComparable("a"),
449         new LegacyComparable("b"),
450         new LegacyComparable("r"),
451         new LegacyComparable("f"),
452         new LegacyComparable("o")).inOrder();
453     assertEquals(Ordering.natural(), multimap.keyComparator());
454     assertEquals(Ordering.natural(), multimap.valueComparator());
455     SerializableTester.reserializeAndAssert(multimap);
456   }
457 
testTreeMultimapAsMapSorted()458   public void testTreeMultimapAsMapSorted() {
459     TreeMultimap<String, Integer> multimap = createPopulate();
460     SortedMap<String, Collection<Integer>> asMap = multimap.asMap();
461     assertEquals(Ordering.natural(), asMap.comparator());
462     assertEquals("foo", asMap.firstKey());
463     assertEquals("tree", asMap.lastKey());
464     Set<Integer> fooValues = ImmutableSet.of(1, 3, 7);
465     Set<Integer> googleValues = ImmutableSet.of(2, 6);
466     Set<Integer> treeValues = ImmutableSet.of(4, 0);
467     assertEquals(ImmutableMap.of("google", googleValues, "tree", treeValues),
468         asMap.tailMap("g"));
469     assertEquals(ImmutableMap.of("google", googleValues, "foo", fooValues),
470         asMap.headMap("h"));
471     assertEquals(ImmutableMap.of("google", googleValues),
472         asMap.subMap("g", "h"));
473   }
474 
testTailSetClear()475   public void testTailSetClear() {
476     TreeMultimap<String, Integer> multimap = TreeMultimap.create();
477     multimap.put("a", 1);
478     multimap.put("a", 11);
479     multimap.put("b", 2);
480     multimap.put("c", 3);
481     multimap.put("d", 4);
482     multimap.put("e", 5);
483     multimap.put("e", 55);
484 
485     multimap.keySet().tailSet("d").clear();
486     assertEquals(ImmutableSet.of("a", "b", "c"), multimap.keySet());
487     assertEquals(4, multimap.size());
488     assertEquals(4, multimap.values().size());
489     assertEquals(4, multimap.keys().size());
490   }
491 
492   @GwtIncompatible("reflection")
testKeySetBridgeMethods()493   public void testKeySetBridgeMethods() {
494     for (Method m : TreeMultimap.class.getMethods()) {
495       if (m.getName().equals("keySet") && m.getReturnType().equals(SortedSet.class)) {
496         return;
497       }
498     }
499     fail("No bridge method found");
500   }
501 
502   @GwtIncompatible("reflection")
testAsMapBridgeMethods()503   public void testAsMapBridgeMethods() {
504     for (Method m : TreeMultimap.class.getMethods()) {
505       if (m.getName().equals("asMap") && m.getReturnType().equals(SortedMap.class)) {
506         return;
507       }
508     }
509   }
510 
511   @GwtIncompatible("reflection")
testGetBridgeMethods()512   public void testGetBridgeMethods() {
513     for (Method m : TreeMultimap.class.getMethods()) {
514       if (m.getName().equals("get") && m.getReturnType().equals(SortedSet.class)) {
515         return;
516       }
517     }
518     fail("No bridge method found");
519   }
520 }
521