001/*
002 * Copyright (C) 2011 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the
010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
011 * express or implied. See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014
015package com.google.common.collect.testing.google;
016
017import static com.google.common.collect.BoundType.CLOSED;
018import static com.google.common.collect.BoundType.OPEN;
019import static com.google.common.collect.testing.Helpers.copyToList;
020import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD;
021import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
022import static com.google.common.collect.testing.features.CollectionSize.ONE;
023import static com.google.common.collect.testing.features.CollectionSize.SEVERAL;
024import static com.google.common.collect.testing.features.CollectionSize.ZERO;
025
026import com.google.common.annotations.GwtCompatible;
027import com.google.common.collect.BoundType;
028import com.google.common.collect.Iterators;
029import com.google.common.collect.Multiset;
030import com.google.common.collect.Multiset.Entry;
031import com.google.common.collect.Multisets;
032import com.google.common.collect.SortedMultiset;
033import com.google.common.collect.testing.features.CollectionFeature;
034import com.google.common.collect.testing.features.CollectionSize;
035import java.util.ArrayList;
036import java.util.Arrays;
037import java.util.Collections;
038import java.util.List;
039import java.util.NoSuchElementException;
040import org.junit.Ignore;
041
042/**
043 * Tester for navigation of SortedMultisets.
044 *
045 * @author Louis Wasserman
046 */
047@GwtCompatible
048@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
049public class MultisetNavigationTester<E> extends AbstractMultisetTester<E> {
050  private SortedMultiset<E> sortedMultiset;
051  private List<E> entries;
052  private Entry<E> a;
053  private Entry<E> b;
054  private Entry<E> c;
055
056  /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */
057  static <T> SortedMultiset<T> cast(Multiset<T> iterable) {
058    return (SortedMultiset<T>) iterable;
059  }
060
061  @Override
062  public void setUp() throws Exception {
063    super.setUp();
064    sortedMultiset = cast(getMultiset());
065    entries =
066        copyToList(
067            getSubjectGenerator()
068                .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements()));
069    Collections.sort(entries, sortedMultiset.comparator());
070
071    // some tests assume SEVERAL == 3
072    if (entries.size() >= 1) {
073      a = Multisets.immutableEntry(entries.get(0), sortedMultiset.count(entries.get(0)));
074      if (entries.size() >= 3) {
075        b = Multisets.immutableEntry(entries.get(1), sortedMultiset.count(entries.get(1)));
076        c = Multisets.immutableEntry(entries.get(2), sortedMultiset.count(entries.get(2)));
077      }
078    }
079  }
080
081  /** Resets the contents of sortedMultiset to have entries a, c, for the navigation tests. */
082  @SuppressWarnings("unchecked")
083  // Needed to stop Eclipse whining
084  private void resetWithHole() {
085    List<E> container = new ArrayList<E>();
086    container.addAll(Collections.nCopies(a.getCount(), a.getElement()));
087    container.addAll(Collections.nCopies(c.getCount(), c.getElement()));
088    super.resetContainer(getSubjectGenerator().create(container.toArray()));
089    sortedMultiset = (SortedMultiset<E>) getMultiset();
090  }
091
092  @CollectionSize.Require(ZERO)
093  public void testEmptyMultisetFirst() {
094    assertNull(sortedMultiset.firstEntry());
095    try {
096      sortedMultiset.elementSet().first();
097      fail();
098    } catch (NoSuchElementException e) {
099    }
100  }
101
102  @CollectionFeature.Require(SUPPORTS_REMOVE)
103  @CollectionSize.Require(ZERO)
104  public void testEmptyMultisetPollFirst() {
105    assertNull(sortedMultiset.pollFirstEntry());
106  }
107
108  @CollectionSize.Require(ZERO)
109  public void testEmptyMultisetNearby() {
110    for (BoundType type : BoundType.values()) {
111      assertNull(sortedMultiset.headMultiset(e0(), type).lastEntry());
112      assertNull(sortedMultiset.tailMultiset(e0(), type).firstEntry());
113    }
114  }
115
116  @CollectionSize.Require(ZERO)
117  public void testEmptyMultisetLast() {
118    assertNull(sortedMultiset.lastEntry());
119    try {
120      assertNull(sortedMultiset.elementSet().last());
121      fail();
122    } catch (NoSuchElementException e) {
123    }
124  }
125
126  @CollectionFeature.Require(SUPPORTS_REMOVE)
127  @CollectionSize.Require(ZERO)
128  public void testEmptyMultisetPollLast() {
129    assertNull(sortedMultiset.pollLastEntry());
130  }
131
132  @CollectionSize.Require(ONE)
133  public void testSingletonMultisetFirst() {
134    assertEquals(a, sortedMultiset.firstEntry());
135  }
136
137  @CollectionFeature.Require(SUPPORTS_REMOVE)
138  @CollectionSize.Require(ONE)
139  public void testSingletonMultisetPollFirst() {
140    assertEquals(a, sortedMultiset.pollFirstEntry());
141    assertTrue(sortedMultiset.isEmpty());
142  }
143
144  @CollectionSize.Require(ONE)
145  public void testSingletonMultisetNearby() {
146    assertNull(sortedMultiset.headMultiset(e0(), OPEN).lastEntry());
147    assertNull(sortedMultiset.tailMultiset(e0(), OPEN).lastEntry());
148
149    assertEquals(a, sortedMultiset.headMultiset(e0(), CLOSED).lastEntry());
150    assertEquals(a, sortedMultiset.tailMultiset(e0(), CLOSED).firstEntry());
151  }
152
153  @CollectionSize.Require(ONE)
154  public void testSingletonMultisetLast() {
155    assertEquals(a, sortedMultiset.lastEntry());
156  }
157
158  @CollectionFeature.Require(SUPPORTS_REMOVE)
159  @CollectionSize.Require(ONE)
160  public void testSingletonMultisetPollLast() {
161    assertEquals(a, sortedMultiset.pollLastEntry());
162    assertTrue(sortedMultiset.isEmpty());
163  }
164
165  @CollectionSize.Require(SEVERAL)
166  public void testFirst() {
167    assertEquals(a, sortedMultiset.firstEntry());
168  }
169
170  @SuppressWarnings("unchecked")
171  @CollectionFeature.Require(SUPPORTS_REMOVE)
172  @CollectionSize.Require(SEVERAL)
173  public void testPollFirst() {
174    assertEquals(a, sortedMultiset.pollFirstEntry());
175    assertEquals(Arrays.asList(b, c), copyToList(sortedMultiset.entrySet()));
176  }
177
178  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
179  public void testPollFirstUnsupported() {
180    try {
181      sortedMultiset.pollFirstEntry();
182      fail();
183    } catch (UnsupportedOperationException e) {
184    }
185  }
186
187  @CollectionSize.Require(SEVERAL)
188  public void testLower() {
189    resetWithHole();
190    assertEquals(null, sortedMultiset.headMultiset(a.getElement(), OPEN).lastEntry());
191    assertEquals(a, sortedMultiset.headMultiset(b.getElement(), OPEN).lastEntry());
192    assertEquals(a, sortedMultiset.headMultiset(c.getElement(), OPEN).lastEntry());
193  }
194
195  @CollectionSize.Require(SEVERAL)
196  public void testFloor() {
197    resetWithHole();
198    assertEquals(a, sortedMultiset.headMultiset(a.getElement(), CLOSED).lastEntry());
199    assertEquals(a, sortedMultiset.headMultiset(b.getElement(), CLOSED).lastEntry());
200    assertEquals(c, sortedMultiset.headMultiset(c.getElement(), CLOSED).lastEntry());
201  }
202
203  @CollectionSize.Require(SEVERAL)
204  public void testCeiling() {
205    resetWithHole();
206
207    assertEquals(a, sortedMultiset.tailMultiset(a.getElement(), CLOSED).firstEntry());
208    assertEquals(c, sortedMultiset.tailMultiset(b.getElement(), CLOSED).firstEntry());
209    assertEquals(c, sortedMultiset.tailMultiset(c.getElement(), CLOSED).firstEntry());
210  }
211
212  @CollectionSize.Require(SEVERAL)
213  public void testHigher() {
214    resetWithHole();
215    assertEquals(c, sortedMultiset.tailMultiset(a.getElement(), OPEN).firstEntry());
216    assertEquals(c, sortedMultiset.tailMultiset(b.getElement(), OPEN).firstEntry());
217    assertEquals(null, sortedMultiset.tailMultiset(c.getElement(), OPEN).firstEntry());
218  }
219
220  @CollectionSize.Require(SEVERAL)
221  public void testLast() {
222    assertEquals(c, sortedMultiset.lastEntry());
223  }
224
225  @SuppressWarnings("unchecked")
226  @CollectionFeature.Require(SUPPORTS_REMOVE)
227  @CollectionSize.Require(SEVERAL)
228  public void testPollLast() {
229    assertEquals(c, sortedMultiset.pollLastEntry());
230    assertEquals(Arrays.asList(a, b), copyToList(sortedMultiset.entrySet()));
231  }
232
233  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
234  @CollectionSize.Require(SEVERAL)
235  public void testPollLastUnsupported() {
236    try {
237      sortedMultiset.pollLastEntry();
238      fail();
239    } catch (UnsupportedOperationException e) {
240    }
241  }
242
243  @CollectionSize.Require(SEVERAL)
244  public void testDescendingNavigation() {
245    List<Entry<E>> ascending = new ArrayList<>();
246    Iterators.addAll(ascending, sortedMultiset.entrySet().iterator());
247    List<Entry<E>> descending = new ArrayList<>();
248    Iterators.addAll(descending, sortedMultiset.descendingMultiset().entrySet().iterator());
249    Collections.reverse(descending);
250    assertEquals(ascending, descending);
251  }
252
253  void expectAddFailure(SortedMultiset<E> multiset, Entry<E> entry) {
254    try {
255      multiset.add(entry.getElement(), entry.getCount());
256      fail("Expected IllegalArgumentException");
257    } catch (IllegalArgumentException expected) {
258    }
259
260    try {
261      multiset.add(entry.getElement());
262      fail("Expected IllegalArgumentException");
263    } catch (IllegalArgumentException expected) {
264    }
265
266    try {
267      multiset.addAll(Collections.singletonList(entry.getElement()));
268      fail("Expected IllegalArgumentException");
269    } catch (IllegalArgumentException expected) {
270    }
271  }
272
273  void expectRemoveZero(SortedMultiset<E> multiset, Entry<E> entry) {
274    assertEquals(0, multiset.remove(entry.getElement(), entry.getCount()));
275    assertFalse(multiset.remove(entry.getElement()));
276    assertFalse(multiset.elementSet().remove(entry.getElement()));
277  }
278
279  void expectSetCountFailure(SortedMultiset<E> multiset, Entry<E> entry) {
280    try {
281      multiset.setCount(entry.getElement(), multiset.count(entry.getElement()));
282    } catch (IllegalArgumentException acceptable) {
283    }
284    try {
285      multiset.setCount(entry.getElement(), multiset.count(entry.getElement()) + 1);
286      fail("Expected IllegalArgumentException");
287    } catch (IllegalArgumentException expected) {
288    }
289  }
290
291  @CollectionSize.Require(ONE)
292  @CollectionFeature.Require(SUPPORTS_ADD)
293  public void testAddOutOfTailBoundsOne() {
294    expectAddFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
295  }
296
297  @CollectionSize.Require(SEVERAL)
298  @CollectionFeature.Require(SUPPORTS_ADD)
299  public void testAddOutOfTailBoundsSeveral() {
300    expectAddFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
301    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
302    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
303    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
304    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
305    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
306    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
307    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
308    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
309  }
310
311  @CollectionSize.Require(ONE)
312  @CollectionFeature.Require(SUPPORTS_ADD)
313  public void testAddOutOfHeadBoundsOne() {
314    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
315  }
316
317  @CollectionSize.Require(SEVERAL)
318  @CollectionFeature.Require(SUPPORTS_ADD)
319  public void testAddOutOfHeadBoundsSeveral() {
320    expectAddFailure(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
321    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
322    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
323    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
324    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
325    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
326    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
327    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
328    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
329  }
330
331  @CollectionSize.Require(ONE)
332  @CollectionFeature.Require(SUPPORTS_REMOVE)
333  public void testRemoveOutOfTailBoundsOne() {
334    expectRemoveZero(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
335  }
336
337  @CollectionSize.Require(SEVERAL)
338  @CollectionFeature.Require(SUPPORTS_REMOVE)
339  public void testRemoveOutOfTailBoundsSeveral() {
340    expectRemoveZero(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
341    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
342    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
343    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
344    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
345    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
346    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
347    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
348    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
349  }
350
351  @CollectionSize.Require(ONE)
352  @CollectionFeature.Require(SUPPORTS_REMOVE)
353  public void testRemoveOutOfHeadBoundsOne() {
354    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
355  }
356
357  @CollectionSize.Require(SEVERAL)
358  @CollectionFeature.Require(SUPPORTS_REMOVE)
359  public void testRemoveOutOfHeadBoundsSeveral() {
360    expectRemoveZero(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
361    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
362    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
363    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
364    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
365    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
366    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
367    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
368    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
369  }
370
371  @CollectionSize.Require(ONE)
372  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
373  public void testSetCountOutOfTailBoundsOne() {
374    expectSetCountFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
375  }
376
377  @CollectionSize.Require(SEVERAL)
378  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
379  public void testSetCountOutOfTailBoundsSeveral() {
380    expectSetCountFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
381    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
382    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
383    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
384    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
385    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
386    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
387    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
388    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
389  }
390
391  @CollectionSize.Require(ONE)
392  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
393  public void testSetCountOutOfHeadBoundsOne() {
394    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
395  }
396
397  @CollectionSize.Require(SEVERAL)
398  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
399  public void testSetCountOutOfHeadBoundsSeveral() {
400    expectSetCountFailure(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
401    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
402    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
403    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
404    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
405    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
406    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
407    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
408    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
409  }
410
411  @CollectionSize.Require(SEVERAL)
412  @CollectionFeature.Require(SUPPORTS_ADD)
413  public void testAddWithConflictingBounds() {
414    testEmptyRangeSubMultisetSupportingAdd(
415        sortedMultiset.subMultiset(a.getElement(), CLOSED, a.getElement(), OPEN));
416    testEmptyRangeSubMultisetSupportingAdd(
417        sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(), OPEN));
418    testEmptyRangeSubMultisetSupportingAdd(
419        sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(), CLOSED));
420    testEmptyRangeSubMultisetSupportingAdd(
421        sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(), CLOSED));
422    testEmptyRangeSubMultisetSupportingAdd(
423        sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(), OPEN));
424    testEmptyRangeSubMultisetSupportingAdd(
425        sortedMultiset.subMultiset(b.getElement(), OPEN, a.getElement(), OPEN));
426  }
427
428  @CollectionSize.Require(SEVERAL)
429  @CollectionFeature.Require(SUPPORTS_ADD)
430  public void testConflictingBounds() {
431    testEmptyRangeSubMultiset(
432        sortedMultiset.subMultiset(a.getElement(), CLOSED, a.getElement(), OPEN));
433    testEmptyRangeSubMultiset(
434        sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(), OPEN));
435    testEmptyRangeSubMultiset(
436        sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(), CLOSED));
437    testEmptyRangeSubMultiset(
438        sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(), CLOSED));
439    testEmptyRangeSubMultiset(
440        sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(), OPEN));
441    testEmptyRangeSubMultiset(
442        sortedMultiset.subMultiset(b.getElement(), OPEN, a.getElement(), OPEN));
443  }
444
445  public void testEmptyRangeSubMultiset(SortedMultiset<E> multiset) {
446    assertTrue(multiset.isEmpty());
447    assertEquals(0, multiset.size());
448    assertEquals(0, multiset.toArray().length);
449    assertTrue(multiset.entrySet().isEmpty());
450    assertFalse(multiset.iterator().hasNext());
451    assertEquals(0, multiset.entrySet().size());
452    assertEquals(0, multiset.entrySet().toArray().length);
453    assertFalse(multiset.entrySet().iterator().hasNext());
454  }
455
456  @SuppressWarnings("unchecked")
457  public void testEmptyRangeSubMultisetSupportingAdd(SortedMultiset<E> multiset) {
458    for (Entry<E> entry : Arrays.asList(a, b, c)) {
459      expectAddFailure(multiset, entry);
460    }
461  }
462
463  private static int totalSize(Iterable<? extends Entry<?>> entries) {
464    int sum = 0;
465    for (Entry<?> entry : entries) {
466      sum += entry.getCount();
467    }
468    return sum;
469  }
470
471  private enum SubMultisetSpec {
472    TAIL_CLOSED {
473      @Override
474      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
475        return entries.subList(targetEntry, entries.size());
476      }
477
478      @Override
479      <E> SortedMultiset<E> subMultiset(
480          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
481        return multiset.tailMultiset(entries.get(targetEntry).getElement(), CLOSED);
482      }
483    },
484    TAIL_OPEN {
485      @Override
486      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
487        return entries.subList(targetEntry + 1, entries.size());
488      }
489
490      @Override
491      <E> SortedMultiset<E> subMultiset(
492          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
493        return multiset.tailMultiset(entries.get(targetEntry).getElement(), OPEN);
494      }
495    },
496    HEAD_CLOSED {
497      @Override
498      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
499        return entries.subList(0, targetEntry + 1);
500      }
501
502      @Override
503      <E> SortedMultiset<E> subMultiset(
504          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
505        return multiset.headMultiset(entries.get(targetEntry).getElement(), CLOSED);
506      }
507    },
508    HEAD_OPEN {
509      @Override
510      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
511        return entries.subList(0, targetEntry);
512      }
513
514      @Override
515      <E> SortedMultiset<E> subMultiset(
516          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
517        return multiset.headMultiset(entries.get(targetEntry).getElement(), OPEN);
518      }
519    };
520
521    abstract <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries);
522
523    abstract <E> SortedMultiset<E> subMultiset(
524        SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry);
525  }
526
527  private void testSubMultisetEntrySet(SubMultisetSpec spec) {
528    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
529    for (int i = 0; i < entries.size(); i++) {
530      List<Entry<E>> expected = spec.expectedEntries(i, entries);
531      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
532      assertEquals(expected, copyToList(subMultiset.entrySet()));
533    }
534  }
535
536  private void testSubMultisetSize(SubMultisetSpec spec) {
537    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
538    for (int i = 0; i < entries.size(); i++) {
539      List<Entry<E>> expected = spec.expectedEntries(i, entries);
540      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
541      assertEquals(totalSize(expected), subMultiset.size());
542    }
543  }
544
545  private void testSubMultisetDistinctElements(SubMultisetSpec spec) {
546    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
547    for (int i = 0; i < entries.size(); i++) {
548      List<Entry<E>> expected = spec.expectedEntries(i, entries);
549      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
550      assertEquals(expected.size(), subMultiset.entrySet().size());
551      assertEquals(expected.size(), subMultiset.elementSet().size());
552    }
553  }
554
555  public void testTailClosedEntrySet() {
556    testSubMultisetEntrySet(SubMultisetSpec.TAIL_CLOSED);
557  }
558
559  public void testTailClosedSize() {
560    testSubMultisetSize(SubMultisetSpec.TAIL_CLOSED);
561  }
562
563  public void testTailClosedDistinctElements() {
564    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_CLOSED);
565  }
566
567  public void testTailOpenEntrySet() {
568    testSubMultisetEntrySet(SubMultisetSpec.TAIL_OPEN);
569  }
570
571  public void testTailOpenSize() {
572    testSubMultisetSize(SubMultisetSpec.TAIL_OPEN);
573  }
574
575  public void testTailOpenDistinctElements() {
576    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_OPEN);
577  }
578
579  public void testHeadClosedEntrySet() {
580    testSubMultisetEntrySet(SubMultisetSpec.HEAD_CLOSED);
581  }
582
583  public void testHeadClosedSize() {
584    testSubMultisetSize(SubMultisetSpec.HEAD_CLOSED);
585  }
586
587  public void testHeadClosedDistinctElements() {
588    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_CLOSED);
589  }
590
591  public void testHeadOpenEntrySet() {
592    testSubMultisetEntrySet(SubMultisetSpec.HEAD_OPEN);
593  }
594
595  public void testHeadOpenSize() {
596    testSubMultisetSize(SubMultisetSpec.HEAD_OPEN);
597  }
598
599  public void testHeadOpenDistinctElements() {
600    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_OPEN);
601  }
602
603  @CollectionSize.Require(SEVERAL)
604  @CollectionFeature.Require(SUPPORTS_REMOVE)
605  public void testClearTailOpen() {
606    List<Entry<E>> expected =
607        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
608    sortedMultiset.tailMultiset(b.getElement(), OPEN).clear();
609    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
610  }
611
612  @CollectionSize.Require(SEVERAL)
613  @CollectionFeature.Require(SUPPORTS_REMOVE)
614  public void testClearTailOpenEntrySet() {
615    List<Entry<E>> expected =
616        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
617    sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet().clear();
618    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
619  }
620
621  @CollectionSize.Require(SEVERAL)
622  @CollectionFeature.Require(SUPPORTS_REMOVE)
623  public void testClearTailClosed() {
624    List<Entry<E>> expected =
625        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
626    sortedMultiset.tailMultiset(b.getElement(), CLOSED).clear();
627    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
628  }
629
630  @CollectionSize.Require(SEVERAL)
631  @CollectionFeature.Require(SUPPORTS_REMOVE)
632  public void testClearTailClosedEntrySet() {
633    List<Entry<E>> expected =
634        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
635    sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet().clear();
636    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
637  }
638
639  @CollectionSize.Require(SEVERAL)
640  @CollectionFeature.Require(SUPPORTS_REMOVE)
641  public void testClearHeadOpen() {
642    List<Entry<E>> expected =
643        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
644    sortedMultiset.headMultiset(b.getElement(), OPEN).clear();
645    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
646  }
647
648  @CollectionSize.Require(SEVERAL)
649  @CollectionFeature.Require(SUPPORTS_REMOVE)
650  public void testClearHeadOpenEntrySet() {
651    List<Entry<E>> expected =
652        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
653    sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet().clear();
654    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
655  }
656
657  @CollectionSize.Require(SEVERAL)
658  @CollectionFeature.Require(SUPPORTS_REMOVE)
659  public void testClearHeadClosed() {
660    List<Entry<E>> expected =
661        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
662    sortedMultiset.headMultiset(b.getElement(), CLOSED).clear();
663    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
664  }
665
666  @CollectionSize.Require(SEVERAL)
667  @CollectionFeature.Require(SUPPORTS_REMOVE)
668  public void testClearHeadClosedEntrySet() {
669    List<Entry<E>> expected =
670        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
671    sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet().clear();
672    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
673  }
674}