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