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