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;
040
041/**
042 * Tester for navigation of SortedMultisets.
043 *
044 * @author Louis Wasserman
045 */
046@GwtCompatible
047public class MultisetNavigationTester<E> extends AbstractMultisetTester<E> {
048  private SortedMultiset<E> sortedMultiset;
049  private List<E> entries;
050  private Entry<E> a;
051  private Entry<E> b;
052  private Entry<E> c;
053
054  /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */
055  static <T> SortedMultiset<T> cast(Multiset<T> iterable) {
056    return (SortedMultiset<T>) iterable;
057  }
058
059  @Override
060  public void setUp() throws Exception {
061    super.setUp();
062    sortedMultiset = cast(getMultiset());
063    entries =
064        copyToList(
065            getSubjectGenerator()
066                .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements()));
067    Collections.sort(entries, sortedMultiset.comparator());
068
069    // some tests assume SEVERAL == 3
070    if (entries.size() >= 1) {
071      a = Multisets.immutableEntry(entries.get(0), sortedMultiset.count(entries.get(0)));
072      if (entries.size() >= 3) {
073        b = Multisets.immutableEntry(entries.get(1), sortedMultiset.count(entries.get(1)));
074        c = Multisets.immutableEntry(entries.get(2), sortedMultiset.count(entries.get(2)));
075      }
076    }
077  }
078
079  /** Resets the contents of sortedMultiset to have entries a, c, for the navigation tests. */
080  @SuppressWarnings("unchecked")
081  // Needed to stop Eclipse whining
082  private void resetWithHole() {
083    List<E> container = new ArrayList<E>();
084    container.addAll(Collections.nCopies(a.getCount(), a.getElement()));
085    container.addAll(Collections.nCopies(c.getCount(), c.getElement()));
086    super.resetContainer(getSubjectGenerator().create(container.toArray()));
087    sortedMultiset = (SortedMultiset<E>) getMultiset();
088  }
089
090  @CollectionSize.Require(ZERO)
091  public void testEmptyMultisetFirst() {
092    assertNull(sortedMultiset.firstEntry());
093    try {
094      sortedMultiset.elementSet().first();
095      fail();
096    } catch (NoSuchElementException e) {
097    }
098  }
099
100  @CollectionFeature.Require(SUPPORTS_REMOVE)
101  @CollectionSize.Require(ZERO)
102  public void testEmptyMultisetPollFirst() {
103    assertNull(sortedMultiset.pollFirstEntry());
104  }
105
106  @CollectionSize.Require(ZERO)
107  public void testEmptyMultisetNearby() {
108    for (BoundType type : BoundType.values()) {
109      assertNull(sortedMultiset.headMultiset(e0(), type).lastEntry());
110      assertNull(sortedMultiset.tailMultiset(e0(), type).firstEntry());
111    }
112  }
113
114  @CollectionSize.Require(ZERO)
115  public void testEmptyMultisetLast() {
116    assertNull(sortedMultiset.lastEntry());
117    try {
118      assertNull(sortedMultiset.elementSet().last());
119      fail();
120    } catch (NoSuchElementException e) {
121    }
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  @SuppressWarnings("unchecked")
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  @SuppressWarnings("unchecked")
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  @SuppressWarnings("unchecked")
455  public void testEmptyRangeSubMultisetSupportingAdd(SortedMultiset<E> multiset) {
456    for (Entry<E> entry : Arrays.asList(a, b, c)) {
457      expectAddFailure(multiset, entry);
458    }
459  }
460
461  private static int totalSize(Iterable<? extends Entry<?>> entries) {
462    int sum = 0;
463    for (Entry<?> entry : entries) {
464      sum += entry.getCount();
465    }
466    return sum;
467  }
468
469  private enum SubMultisetSpec {
470    TAIL_CLOSED {
471      @Override
472      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
473        return entries.subList(targetEntry, entries.size());
474      }
475
476      @Override
477      <E> SortedMultiset<E> subMultiset(
478          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
479        return multiset.tailMultiset(entries.get(targetEntry).getElement(), CLOSED);
480      }
481    },
482    TAIL_OPEN {
483      @Override
484      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
485        return entries.subList(targetEntry + 1, entries.size());
486      }
487
488      @Override
489      <E> SortedMultiset<E> subMultiset(
490          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
491        return multiset.tailMultiset(entries.get(targetEntry).getElement(), OPEN);
492      }
493    },
494    HEAD_CLOSED {
495      @Override
496      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
497        return entries.subList(0, targetEntry + 1);
498      }
499
500      @Override
501      <E> SortedMultiset<E> subMultiset(
502          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
503        return multiset.headMultiset(entries.get(targetEntry).getElement(), CLOSED);
504      }
505    },
506    HEAD_OPEN {
507      @Override
508      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
509        return entries.subList(0, targetEntry);
510      }
511
512      @Override
513      <E> SortedMultiset<E> subMultiset(
514          SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry) {
515        return multiset.headMultiset(entries.get(targetEntry).getElement(), OPEN);
516      }
517    };
518
519    abstract <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries);
520
521    abstract <E> SortedMultiset<E> subMultiset(
522        SortedMultiset<E> multiset, List<Entry<E>> entries, int targetEntry);
523  }
524
525  private void testSubMultisetEntrySet(SubMultisetSpec spec) {
526    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
527    for (int i = 0; i < entries.size(); i++) {
528      List<Entry<E>> expected = spec.expectedEntries(i, entries);
529      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
530      assertEquals(expected, copyToList(subMultiset.entrySet()));
531    }
532  }
533
534  private void testSubMultisetSize(SubMultisetSpec spec) {
535    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
536    for (int i = 0; i < entries.size(); i++) {
537      List<Entry<E>> expected = spec.expectedEntries(i, entries);
538      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
539      assertEquals(totalSize(expected), subMultiset.size());
540    }
541  }
542
543  private void testSubMultisetDistinctElements(SubMultisetSpec spec) {
544    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
545    for (int i = 0; i < entries.size(); i++) {
546      List<Entry<E>> expected = spec.expectedEntries(i, entries);
547      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
548      assertEquals(expected.size(), subMultiset.entrySet().size());
549      assertEquals(expected.size(), subMultiset.elementSet().size());
550    }
551  }
552
553  public void testTailClosedEntrySet() {
554    testSubMultisetEntrySet(SubMultisetSpec.TAIL_CLOSED);
555  }
556
557  public void testTailClosedSize() {
558    testSubMultisetSize(SubMultisetSpec.TAIL_CLOSED);
559  }
560
561  public void testTailClosedDistinctElements() {
562    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_CLOSED);
563  }
564
565  public void testTailOpenEntrySet() {
566    testSubMultisetEntrySet(SubMultisetSpec.TAIL_OPEN);
567  }
568
569  public void testTailOpenSize() {
570    testSubMultisetSize(SubMultisetSpec.TAIL_OPEN);
571  }
572
573  public void testTailOpenDistinctElements() {
574    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_OPEN);
575  }
576
577  public void testHeadClosedEntrySet() {
578    testSubMultisetEntrySet(SubMultisetSpec.HEAD_CLOSED);
579  }
580
581  public void testHeadClosedSize() {
582    testSubMultisetSize(SubMultisetSpec.HEAD_CLOSED);
583  }
584
585  public void testHeadClosedDistinctElements() {
586    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_CLOSED);
587  }
588
589  public void testHeadOpenEntrySet() {
590    testSubMultisetEntrySet(SubMultisetSpec.HEAD_OPEN);
591  }
592
593  public void testHeadOpenSize() {
594    testSubMultisetSize(SubMultisetSpec.HEAD_OPEN);
595  }
596
597  public void testHeadOpenDistinctElements() {
598    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_OPEN);
599  }
600
601  @CollectionSize.Require(SEVERAL)
602  @CollectionFeature.Require(SUPPORTS_REMOVE)
603  public void testClearTailOpen() {
604    List<Entry<E>> expected =
605        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
606    sortedMultiset.tailMultiset(b.getElement(), OPEN).clear();
607    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
608  }
609
610  @CollectionSize.Require(SEVERAL)
611  @CollectionFeature.Require(SUPPORTS_REMOVE)
612  public void testClearTailOpenEntrySet() {
613    List<Entry<E>> expected =
614        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
615    sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet().clear();
616    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
617  }
618
619  @CollectionSize.Require(SEVERAL)
620  @CollectionFeature.Require(SUPPORTS_REMOVE)
621  public void testClearTailClosed() {
622    List<Entry<E>> expected =
623        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
624    sortedMultiset.tailMultiset(b.getElement(), CLOSED).clear();
625    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
626  }
627
628  @CollectionSize.Require(SEVERAL)
629  @CollectionFeature.Require(SUPPORTS_REMOVE)
630  public void testClearTailClosedEntrySet() {
631    List<Entry<E>> expected =
632        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
633    sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet().clear();
634    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
635  }
636
637  @CollectionSize.Require(SEVERAL)
638  @CollectionFeature.Require(SUPPORTS_REMOVE)
639  public void testClearHeadOpen() {
640    List<Entry<E>> expected =
641        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
642    sortedMultiset.headMultiset(b.getElement(), OPEN).clear();
643    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
644  }
645
646  @CollectionSize.Require(SEVERAL)
647  @CollectionFeature.Require(SUPPORTS_REMOVE)
648  public void testClearHeadOpenEntrySet() {
649    List<Entry<E>> expected =
650        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
651    sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet().clear();
652    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
653  }
654
655  @CollectionSize.Require(SEVERAL)
656  @CollectionFeature.Require(SUPPORTS_REMOVE)
657  public void testClearHeadClosed() {
658    List<Entry<E>> expected =
659        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
660    sortedMultiset.headMultiset(b.getElement(), CLOSED).clear();
661    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
662  }
663
664  @CollectionSize.Require(SEVERAL)
665  @CollectionFeature.Require(SUPPORTS_REMOVE)
666  public void testClearHeadClosedEntrySet() {
667    List<Entry<E>> expected =
668        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
669    sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet().clear();
670    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
671  }
672}