001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.collection;
017
018import static org.hamcrest.core.Is.is;
019import static org.junit.Assert.assertEquals;
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertNotEquals;
022import static org.junit.Assert.assertThat;
023import static org.junit.Assert.assertTrue;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import org.junit.After;
034import org.junit.Before;
035import org.junit.Test;
036import org.modeshape.common.FixFor;
037
038public abstract class AbstractMultimapTest {
039
040    protected Multimap<String, String> multimap;
041    protected String[] keys;
042    protected String[] values;
043
044    @Before
045    public void beforeEach() {
046        multimap = createMultimap();
047        keys = new String[] {"key1", "key2", "key3", "key4"};
048        values = new String[] {"value1", "value2", "value3", "value4", "value5", "value6"};
049    }
050
051    @After
052    public void afterEach() {
053        multimap = null;
054    }
055
056    protected abstract <K, V> Multimap<K, V> createMultimap();
057
058    protected abstract boolean valuesAllowDuplicates();
059
060    @Test
061    public void shouldBeEmptyAfterCreation() {
062        assertThat(multimap.isEmpty(), is(true));
063    }
064
065    @Test
066    public void shouldHaveZeroSizeAfterCreation() {
067        assertThat(multimap.size(), is(0));
068    }
069
070    @Test
071    public void shouldNotBeEmptyAfterAddingKeyValuePairToEmptyCollection() {
072        multimap.put(keys[0], values[0]);
073        assertThat(multimap.isEmpty(), is(false));
074        assertThat(multimap.size(), is(1));
075        assertKeys(multimap, keys[0]);
076        assertValues(multimap, keys[0], values[0]);
077        assertEntries(multimap, entry(keys[0], values[0]));
078    }
079
080    @Test
081    public void shouldNotBeEmptyAfterAddingKeyAndTwoValuesToEmptyCollection() {
082        multimap.put(keys[0], values[0]);
083        multimap.put(keys[0], values[1]);
084        assertThat(multimap.isEmpty(), is(false));
085        assertThat(multimap.size(), is(2));
086        assertKeys(multimap, keys[0]);
087        assertValues(multimap, keys[0], values[0], values[1]);
088        assertEntries(multimap, entry(keys[0], values[0]), entry(keys[0], values[1]));
089    }
090
091    @Test
092    public void shouldNotBeEmptyAfterAddingMultipleKeyValuePairsToEmptyCollection() {
093        multimap.put(keys[0], values[0]);
094        multimap.put(keys[1], values[1]);
095        assertThat(multimap.isEmpty(), is(false));
096        assertThat(multimap.size(), is(2));
097        assertKeys(multimap, keys[0], keys[1]);
098        assertValues(multimap, keys[0], values[0]);
099        assertValues(multimap, keys[1], values[1]);
100        assertEntries(multimap, entry(keys[0], values[0]), entry(keys[1], values[1]));
101    }
102
103    @Test
104    public void shouldNotBeEmptyAfterAddingMultipleKeyValuePairsMultipleTimesToEmptyCollection() {
105        for (int i = 0; i != 3; ++i) {
106            multimap.put(keys[0], values[0]);
107            multimap.put(keys[1], values[1]);
108        }
109        if (valuesAllowDuplicates()) {
110            assertThat(multimap.isEmpty(), is(false));
111            assertThat(multimap.size(), is(6));
112            assertKeys(multimap, keys[0], keys[1]);
113            assertValues(multimap, keys[0], values[0], values[0], values[0]);
114            assertValues(multimap, keys[1], values[1], values[1], values[1]);
115            Collection<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>();
116            for (int i = 0; i != 3; ++i) {
117                entries.add(entry(keys[0], values[0]));
118                entries.add(entry(keys[1], values[1]));
119            }
120            assertEntries(multimap, entries);
121        } else {
122            assertThat(multimap.isEmpty(), is(false));
123            assertThat(multimap.size(), is(2));
124            assertKeys(multimap, keys[0], keys[1]);
125            assertValues(multimap, keys[0], values[0]);
126            assertValues(multimap, keys[1], values[1]);
127            assertEntries(multimap, entry(keys[0], values[0]), entry(keys[1], values[1]));
128        }
129    }
130
131    @Test
132    public void shouldAllowAddingToCollectionOfValues() {
133        multimap.put(keys[0], values[0]);
134        Collection<String> vals = multimap.get(keys[0]);
135        vals.add(values[1]);
136        vals.add(values[2]);
137        assertThat(multimap.isEmpty(), is(false));
138        assertThat(multimap.size(), is(3));
139        assertKeys(multimap, keys[0]);
140        assertValues(multimap, keys[0], values[0], values[1], values[2]);
141        assertEntries(multimap, entry(keys[0], values[0]), entry(keys[0], values[1]), entry(keys[0], values[2]));
142    }
143
144    @Test
145    @FixFor("MODE-2743")
146    public void shouldDecrementSizeOnValueIteratorRemove() {
147        assertEquals(0, multimap.size());
148        assertTrue(multimap.isEmpty());
149        multimap.put(keys[0], values[0]);
150        assertEquals(1, multimap.size());
151        Iterator<String> iterator = multimap.get(keys[0]).iterator();
152        iterator.next();
153        iterator.remove();
154        assertEquals(0, multimap.size());
155        assertTrue(multimap.isEmpty());
156    }
157
158    @Test
159    public void shouldIterateSuccessfullyWithRemoval() {
160        for (String v : values) {
161            multimap.put(keys[0], v);
162        }
163        for (Iterator<String> iter = multimap.get(keys[0]).iterator(); iter.hasNext();) {
164            iter.next();
165            iter.remove();
166        }
167        assertTrue(multimap.isEmpty());
168    }
169
170    @Test
171    public void shouldSuccessfullyRemoveLastElementFromValueCollection() {
172        multimap.put(keys[0], values[0]);
173        Collection<String> collection = multimap.get(keys[0]);
174        assertTrue(collection.contains(values[0]));
175
176        assertTrue(collection.remove(values[0]));
177        assertFalse(collection.contains(values[0]));
178        assertTrue(collection.isEmpty());
179        assertEquals(0, collection.size());
180        assertTrue(multimap.isEmpty());
181        assertEquals(0, multimap.size());
182
183        assertFalse(collection.remove(values[0]));
184        assertFalse(collection.contains(values[0]));
185        assertTrue(collection.isEmpty());
186        assertEquals(0, collection.size());
187        assertTrue(multimap.isEmpty());
188        assertEquals(0, multimap.size());
189    }
190
191    @Test
192    public void shouldSuccessfullyAddValueToValueCollection() {
193        multimap.put(keys[0], values[0]);
194        Collection<String> collection = multimap.get(keys[0]);
195        assertEquals(1, collection.size());
196        assertFalse(collection.isEmpty());
197        assertEquals(1, multimap.size());
198        assertFalse(multimap.isEmpty());
199
200        assertTrue(collection.add(values[1]));
201        assertEquals(2, collection.size());
202        assertFalse(collection.isEmpty());
203        assertEquals(2, multimap.size());
204        assertFalse(multimap.isEmpty());
205    }
206
207    @Test
208    public void shouldSuccessfullyAddValueToEmptyValueCollection() {
209        Collection<String> collection = multimap.get(keys[0]);
210
211        assertTrue(collection.add(values[0]));
212        assertTrue(collection.contains(values[0]));
213        assertFalse(collection.isEmpty());
214        assertEquals(1, collection.size());
215        assertFalse(multimap.isEmpty());
216        assertEquals(1, multimap.size());
217    }
218
219    @Test
220    public void shouldSuccessfullyAddValueToEmptiedValueCollection() {
221        multimap.put(keys[0], values[0]);
222        Collection<String> collection = multimap.get(keys[0]);
223
224        assertTrue(collection.remove(values[0]));
225        assertFalse(collection.contains(values[0]));
226        assertTrue(collection.isEmpty());
227        assertEquals(0, collection.size());
228        assertTrue(multimap.isEmpty());
229        assertEquals(0, multimap.size());
230
231        assertTrue(collection.add(values[0]));
232        assertTrue(collection.contains(values[0]));
233        assertFalse(collection.isEmpty());
234        assertEquals(1, collection.size());
235        assertFalse(multimap.isEmpty());
236        assertEquals(1, multimap.size());
237    }
238
239    @Test
240    public void shouldSuccessfullyRemoveAllFromValueCollection() {
241        multimap.put(keys[0], values[0]);
242        Collection<String> collection = multimap.get(keys[0]);
243
244        assertTrue(collection.removeAll(Arrays.asList(values)));
245        assertTrue(collection.isEmpty());
246        assertEquals(0, collection.size());
247        assertTrue(multimap.isEmpty());
248        assertEquals(0, multimap.size());
249
250        assertFalse(collection.removeAll(Arrays.asList(values)));
251        assertTrue(collection.isEmpty());
252        assertEquals(0, collection.size());
253        assertTrue(multimap.isEmpty());
254        assertEquals(0, multimap.size());
255    }
256
257    @Test
258    public void shouldSuccessfullyAddAllToValueCollection() {
259        multimap.put(keys[0], values[0]);
260        Collection<String> collection = multimap.get(keys[0]);
261
262        collection.addAll(Arrays.asList(values));
263
264        int expectedNumberOfValues = values.length;
265        if (valuesAllowDuplicates()) {
266            expectedNumberOfValues++;
267        }
268
269        assertEquals(expectedNumberOfValues, collection.size());
270        assertFalse(collection.isEmpty());
271
272        assertEquals(expectedNumberOfValues, multimap.size());
273        assertFalse(multimap.isEmpty());
274    }
275
276    @Test
277    public void shouldSuccessfullyClearValueCollection() {
278        multimap.put(keys[0], values[0]);
279        Collection<String> collection = multimap.get(keys[0]);
280
281        collection.clear();
282        assertTrue(collection.isEmpty());
283        assertEquals(0, collection.size());
284        assertTrue(multimap.isEmpty());
285        assertEquals(0, multimap.size());
286
287        collection.clear();
288        assertTrue(collection.isEmpty());
289        assertEquals(0, collection.size());
290        assertTrue(multimap.isEmpty());
291        assertEquals(0, multimap.size());
292    }
293
294    @Test
295    public void shouldSuccessfullyAddAllToEmptyValueCollection() {
296        Collection<String> collection = multimap.get(keys[0]);
297
298        assertTrue(collection.addAll(Arrays.asList(values)));
299        assertEquals(values.length, collection.size());
300        assertFalse(collection.isEmpty());
301        assertEquals(values.length, multimap.size());
302        assertFalse(multimap.isEmpty());
303    }
304
305    @Test
306    public void shouldSuccessfullyAddAllToEmptiedValueCollection() {
307        multimap.put(keys[0], values[0]);
308        Collection<String> collection = multimap.get(keys[0]);
309        collection.clear();
310
311        assertTrue(collection.addAll(Arrays.asList(values)));
312        assertEquals(values.length, collection.size());
313        assertFalse(collection.isEmpty());
314        assertEquals(values.length, multimap.size());
315        assertFalse(multimap.isEmpty());
316    }
317
318    @Test
319    public void shouldRetainAllInValueCollection() {
320        for (String value : values) {
321            multimap.put(keys[0], value);
322        }
323        assertEquals(values.length, multimap.size());
324        Collection<String> collection = multimap.get(keys[0]);
325        assertEquals(values.length, collection.size());
326
327        assertFalse(collection.retainAll(Arrays.asList(values)));
328        assertEquals(values.length, multimap.size());
329        assertEquals(values.length, collection.size());
330
331        assertTrue(collection.retainAll(Collections.singleton(values[0])));
332        assertEquals(1, multimap.size());
333        assertEquals(1, collection.size());
334    }
335
336    @Test
337    public void shouldRetainNoneInValueCollection() {
338        for (String value : values) {
339            multimap.put(keys[0], value);
340        }
341        assertEquals(values.length, multimap.size());
342        Collection<String> collection = multimap.get(keys[0]);
343        assertEquals(values.length, collection.size());
344
345        assertTrue(collection.retainAll(Collections.emptySet()));
346        assertTrue(multimap.isEmpty());
347        assertEquals(0, multimap.size());
348        assertTrue(collection.isEmpty());
349        assertEquals(0, collection.size());
350    }
351
352    @Test
353    public void shouldSuccessfullyRetainAllInValueCollection() {
354        multimap.put(keys[0], values[0]);
355        Collection<String> collection = multimap.get(keys[0]);
356
357        assertFalse(collection.retainAll(Arrays.asList(values)));
358        assertFalse(collection.isEmpty());
359        assertEquals(1, collection.size());
360        assertFalse(multimap.isEmpty());
361        assertEquals(1, multimap.size());
362    }
363
364    @Test
365    public void shouldSuccessfullyRetainAllInEmptyValueCollection() {
366        Collection<String> collection = multimap.get(keys[0]);
367
368        assertFalse(collection.retainAll(Arrays.asList(values)));
369        assertTrue(collection.isEmpty());
370        assertEquals(0, collection.size());
371        assertTrue(multimap.isEmpty());
372        assertEquals(0, multimap.size());
373    }
374
375    @Test
376    public void shouldSuccessfullyRetainAllInEmptiedValueCollection() {
377        multimap.put(keys[0], values[0]);
378        Collection<String> collection = multimap.get(keys[0]);
379        collection.clear();
380
381        assertFalse(collection.retainAll(Arrays.asList(values)));
382        assertTrue(collection.isEmpty());
383        assertEquals(0, collection.size());
384        assertTrue(multimap.isEmpty());
385        assertEquals(0, multimap.size());
386    }
387
388    @Test
389    public void shouldProduceHashCodeOfEmptyValueCollection() {
390        multimap.get(keys[0]).hashCode();
391    }
392
393    @Test
394    public void shouldProduceHashCodeOfEmptiedValueCollection() {
395        multimap.put(keys[0], values[0]);
396        Collection<String> collection = multimap.get(keys[0]);
397        collection.clear();
398        collection.hashCode();
399    }
400
401    @Test
402    public void shouldCompareEqualityOfEmptyValueCollection() {
403        Collection<String> collection = multimap.get(keys[0]);
404
405        assertNotEquals(this, collection);
406        assertNotEquals(collection, this);
407        if (collection instanceof List<?>) {
408            assertEquals(Collections.emptyList(), collection);
409            assertEquals(collection, Collections.emptyList());
410        } else if (collection instanceof Set<?>) {
411            assertEquals(Collections.emptySet(), collection);
412            assertEquals(collection, Collections.emptySet());
413        }
414    }
415
416    @Test
417    public void shouldCompareEqualityOfEmptiedValueCollection() {
418        multimap.put(keys[0], values[0]);
419        Collection<String> collection = multimap.get(keys[0]);
420        collection.clear();
421
422        assertNotEquals(this, collection);
423        assertNotEquals(collection, this);
424        if (collection instanceof List<?>) {
425            assertEquals(Collections.emptyList(), collection);
426            assertEquals(collection, Collections.emptyList());
427        } else if (collection instanceof Set<?>) {
428            assertEquals(Collections.emptySet(), collection);
429            assertEquals(collection, Collections.emptySet());
430        }
431    }
432
433    protected <K, V> Map.Entry<K, V> entry( K key,
434                                            V value ) {
435        return new ImmutableMapEntry<K, V>(key, value);
436    }
437
438    protected <K, V> void assertEntries( Multimap<K, V> multimap,
439                                         Map.Entry<K, V> entry ) {
440        assertEntries(multimap, java.util.Collections.singletonList(entry));
441    }
442
443    @SafeVarargs
444    protected final <K, V> void assertEntries( Multimap<K, V> multimap, Map.Entry<K, V>... entries ) {
445        assertEntries(multimap, Arrays.asList(entries));
446    }
447
448    protected <K, V> void assertEntries( Multimap<K, V> multimap,
449                                         Collection<Map.Entry<K, V>> entries ) {
450        Collection<Map.Entry<K, V>> actualEntries = multimap.entries();
451        assertThat(actualEntries.size(), is(entries.size()));
452        assertThat(actualEntries.containsAll(entries), is(true));
453        assertThat(entries.containsAll(actualEntries), is(true));
454    }
455
456    protected <K, V> void assertKeys( Multimap<K, V> multimap,
457                                      K key ) {
458        assertKeys(multimap, java.util.Collections.singletonList(key));
459    }
460
461    @SafeVarargs
462    protected final <K, V> void assertKeys( Multimap<K, V> multimap, K... keys ) {
463        assertKeys(multimap, Arrays.asList(keys));
464    }
465
466    protected <K, V> void assertKeys( Multimap<K, V> multimap,
467                                      Collection<K> expectedKeys ) {
468        Set<K> actualKeys = multimap.keySet();
469        assertThat(actualKeys.size(), is(expectedKeys.size()));
470        assertThat(actualKeys.containsAll(expectedKeys), is(true));
471        assertThat(expectedKeys.containsAll(actualKeys), is(true));
472    }
473
474    protected <K, V> void assertValues( Multimap<K, V> multimap,
475                                        K key,
476                                        V value ) {
477        Collection<V> expectedValues = null;
478        if (valuesAllowDuplicates()) {
479            expectedValues = java.util.Collections.singletonList(value);
480        } else {
481            expectedValues = java.util.Collections.singleton(value);
482        }
483        assertValues(multimap, key, expectedValues);
484    }
485
486    @SafeVarargs
487    protected final <K, V> void assertValues( Multimap<K, V> multimap, K key, V... values ) {
488        Collection<V> expectedValues = null;
489        if (valuesAllowDuplicates()) {
490            expectedValues = Arrays.asList(values);
491        } else {
492            expectedValues = new HashSet<V>(Arrays.asList(values));
493        }
494        assertValues(multimap, key, expectedValues);
495    }
496
497    protected <K, V> void assertValues( Multimap<K, V> multimap,
498                                        K key,
499                                        Collection<V> expectedValues ) {
500        Collection<V> actualValues = multimap.get(key);
501        assertThat(actualValues.size(), is(expectedValues.size()));
502        if (actualValues instanceof List) {
503            assertThat(actualValues, is(expectedValues));
504            Iterator<V> actualIter = actualValues.iterator();
505            Iterator<V> expectedIter = expectedValues.iterator();
506            while (expectedIter.hasNext()) {
507                V actual = actualIter.next();
508                V expected = expectedIter.next();
509                assertThat(actual, is(expected));
510            }
511        } else {
512            assertThat(actualValues.containsAll(expectedValues), is(true));
513            assertThat(expectedValues.containsAll(actualValues), is(true));
514        }
515    }
516}