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