MultisetNavigationTester.java revision dbd967a6e5c96cc1a97c5521f88dc1564ba2f81b
1/*
2 * Copyright (C) 2011 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11 * express or implied. See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15package com.google.common.collect.testing.google;
16
17import static com.google.common.collect.BoundType.CLOSED;
18import static com.google.common.collect.BoundType.OPEN;
19import static com.google.common.collect.testing.Helpers.copyToList;
20import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD;
21import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
22import static com.google.common.collect.testing.features.CollectionSize.ONE;
23import static com.google.common.collect.testing.features.CollectionSize.SEVERAL;
24import static com.google.common.collect.testing.features.CollectionSize.ZERO;
25
26import com.google.common.annotations.GwtCompatible;
27import com.google.common.collect.BoundType;
28import com.google.common.collect.Iterators;
29import com.google.common.collect.Multiset;
30import com.google.common.collect.Multiset.Entry;
31import com.google.common.collect.Multisets;
32import com.google.common.collect.SortedMultiset;
33import com.google.common.collect.testing.features.CollectionFeature;
34import com.google.common.collect.testing.features.CollectionSize;
35
36import java.util.ArrayList;
37import java.util.Arrays;
38import java.util.Collections;
39import java.util.List;
40import java.util.NoSuchElementException;
41
42/**
43 * Tester for navigation of SortedMultisets.
44 *
45 * @author Louis Wasserman
46 */
47@GwtCompatible
48public class MultisetNavigationTester<E> extends AbstractMultisetTester<E> {
49  private SortedMultiset<E> sortedMultiset;
50  private List<E> entries;
51  private Entry<E> a;
52  private Entry<E> b;
53  private Entry<E> c;
54
55  /**
56   * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557
57   */
58  static <T> SortedMultiset<T> cast(Multiset<T> iterable) {
59    return (SortedMultiset<T>) iterable;
60  }
61
62  @Override
63  public void setUp() throws Exception {
64    super.setUp();
65    sortedMultiset = cast(getMultiset());
66    entries =
67        copyToList(getSubjectGenerator().getSampleElements(
68            getSubjectGenerator().getCollectionSize().getNumElements()));
69    Collections.sort(entries, sortedMultiset.comparator());
70
71    // some tests assume SEVERAL == 3
72    if (entries.size() >= 1) {
73      a = Multisets.immutableEntry(entries.get(0), sortedMultiset.count(entries.get(0)));
74      if (entries.size() >= 3) {
75        b = Multisets.immutableEntry(entries.get(1), sortedMultiset.count(entries.get(1)));
76        c = Multisets.immutableEntry(entries.get(2), sortedMultiset.count(entries.get(2)));
77      }
78    }
79  }
80
81  /**
82   * Resets the contents of sortedMultiset to have entries a, c, for the navigation tests.
83   */
84  @SuppressWarnings("unchecked")
85  // Needed to stop Eclipse whining
86  private void resetWithHole() {
87    List<E> container = new ArrayList<E>();
88    container.addAll(Collections.nCopies(a.getCount(), a.getElement()));
89    container.addAll(Collections.nCopies(c.getCount(), c.getElement()));
90    super.resetContainer(getSubjectGenerator().create(container.toArray()));
91    sortedMultiset = (SortedMultiset<E>) getMultiset();
92  }
93
94  @CollectionSize.Require(ZERO)
95  public void testEmptyMultisetFirst() {
96    assertNull(sortedMultiset.firstEntry());
97    try {
98      sortedMultiset.elementSet().first();
99      fail();
100    } catch (NoSuchElementException e) {}
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(samples.e0, type).lastEntry());
113      assertNull(sortedMultiset.tailMultiset(samples.e0, type).firstEntry());
114    }
115  }
116
117  @CollectionSize.Require(ZERO)
118  public void testEmptyMultisetLast() {
119    assertNull(sortedMultiset.lastEntry());
120    try {
121      assertNull(sortedMultiset.elementSet().last());
122      fail();
123    } catch (NoSuchElementException e) {}
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(samples.e0, OPEN).lastEntry());
147    assertNull(sortedMultiset.tailMultiset(samples.e0, OPEN).lastEntry());
148
149    assertEquals(a, sortedMultiset.headMultiset(samples.e0, CLOSED).lastEntry());
150    assertEquals(a, sortedMultiset.tailMultiset(samples.e0, CLOSED).firstEntry());
151  }
152
153  @CollectionSize.Require(ONE)
154  public void testSingletonMultisetLast() {
155    assertEquals(a, sortedMultiset.lastEntry());
156  }
157
158  @CollectionFeature.Require(SUPPORTS_REMOVE)
159  @CollectionSize.Require(ONE)
160  public void testSingletonMultisetPollLast() {
161    assertEquals(a, sortedMultiset.pollLastEntry());
162    assertTrue(sortedMultiset.isEmpty());
163  }
164
165  @CollectionSize.Require(SEVERAL)
166  public void testFirst() {
167    assertEquals(a, sortedMultiset.firstEntry());
168  }
169
170  @SuppressWarnings("unchecked")
171  @CollectionFeature.Require(SUPPORTS_REMOVE)
172  @CollectionSize.Require(SEVERAL)
173  public void testPollFirst() {
174    assertEquals(a, sortedMultiset.pollFirstEntry());
175    assertEquals(Arrays.asList(b, c), copyToList(sortedMultiset.entrySet()));
176  }
177
178  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
179  public void testPollFirstUnsupported() {
180    try {
181      sortedMultiset.pollFirstEntry();
182      fail();
183    } catch (UnsupportedOperationException e) {}
184  }
185
186  @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  @SuppressWarnings("unchecked")
225  @CollectionFeature.Require(SUPPORTS_REMOVE)
226  @CollectionSize.Require(SEVERAL)
227  public void testPollLast() {
228    assertEquals(c, sortedMultiset.pollLastEntry());
229    assertEquals(Arrays.asList(a, b), copyToList(sortedMultiset.entrySet()));
230  }
231
232  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
233  @CollectionSize.Require(SEVERAL)
234  public void testPollLastUnsupported() {
235    try {
236      sortedMultiset.pollLastEntry();
237      fail();
238    } catch (UnsupportedOperationException e) {}
239  }
240
241  @CollectionSize.Require(SEVERAL)
242  public void testDescendingNavigation() {
243    List<Entry<E>> ascending = new ArrayList<Entry<E>>();
244    Iterators.addAll(ascending, sortedMultiset.entrySet().iterator());
245    List<Entry<E>> descending = new ArrayList<Entry<E>>();
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    try {
258      multiset.add(entry.getElement());
259      fail("Expected IllegalArgumentException");
260    } catch (IllegalArgumentException expected) {}
261
262    try {
263      multiset.addAll(Collections.singletonList(entry.getElement()));
264      fail("Expected IllegalArgumentException");
265    } catch (IllegalArgumentException expected) {}
266  }
267
268  void expectRemoveZero(SortedMultiset<E> multiset, Entry<E> entry) {
269    assertEquals(0, multiset.remove(entry.getElement(), entry.getCount()));
270    assertFalse(multiset.remove(entry.getElement()));
271    assertFalse(multiset.elementSet().remove(entry.getElement()));
272  }
273
274  void expectSetCountFailure(SortedMultiset<E> multiset, Entry<E> entry) {
275    try {
276      multiset.setCount(entry.getElement(), multiset.count(entry.getElement()));
277    } catch (IllegalArgumentException acceptable) {}
278    try {
279      multiset.setCount(entry.getElement(), multiset.count(entry.getElement()) + 1);
280      fail("Expected IllegalArgumentException");
281    } catch (IllegalArgumentException expected) {}
282  }
283
284  @CollectionSize.Require(ONE)
285  @CollectionFeature.Require(SUPPORTS_ADD)
286  public void testAddOutOfTailBoundsOne() {
287    expectAddFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
288  }
289
290  @CollectionSize.Require(SEVERAL)
291  @CollectionFeature.Require(SUPPORTS_ADD)
292  public void testAddOutOfTailBoundsSeveral() {
293    expectAddFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
294    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
295    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
296    expectAddFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
297    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
298    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
299    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
300    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
301    expectAddFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
302  }
303
304  @CollectionSize.Require(ONE)
305  @CollectionFeature.Require(SUPPORTS_ADD)
306  public void testAddOutOfHeadBoundsOne() {
307    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
308  }
309
310  @CollectionSize.Require(SEVERAL)
311  @CollectionFeature.Require(SUPPORTS_ADD)
312  public void testAddOutOfHeadBoundsSeveral() {
313    expectAddFailure(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
314    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
315    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
316    expectAddFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
317    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
318    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
319    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
320    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
321    expectAddFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
322  }
323
324  @CollectionSize.Require(ONE)
325  @CollectionFeature.Require(SUPPORTS_REMOVE)
326  public void testRemoveOutOfTailBoundsOne() {
327    expectRemoveZero(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
328  }
329
330  @CollectionSize.Require(SEVERAL)
331  @CollectionFeature.Require(SUPPORTS_REMOVE)
332  public void testRemoveOutOfTailBoundsSeveral() {
333    expectRemoveZero(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
334    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
335    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
336    expectRemoveZero(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
337    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
338    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
339    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
340    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
341    expectRemoveZero(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
342  }
343
344  @CollectionSize.Require(ONE)
345  @CollectionFeature.Require(SUPPORTS_REMOVE)
346  public void testRemoveOutOfHeadBoundsOne() {
347    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
348  }
349
350  @CollectionSize.Require(SEVERAL)
351  @CollectionFeature.Require(SUPPORTS_REMOVE)
352  public void testRemoveOutOfHeadBoundsSeveral() {
353    expectRemoveZero(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
354    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
355    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
356    expectRemoveZero(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
357    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
358    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
359    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
360    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
361    expectRemoveZero(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
362  }
363
364  @CollectionSize.Require(ONE)
365  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
366  public void testSetCountOutOfTailBoundsOne() {
367    expectSetCountFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
368  }
369
370  @CollectionSize.Require(SEVERAL)
371  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
372  public void testSetCountOutOfTailBoundsSeveral() {
373    expectSetCountFailure(sortedMultiset.tailMultiset(a.getElement(), OPEN), a);
374    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), CLOSED), a);
375    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), a);
376    expectSetCountFailure(sortedMultiset.tailMultiset(b.getElement(), OPEN), b);
377    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), a);
378    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), CLOSED), b);
379    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), a);
380    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), b);
381    expectSetCountFailure(sortedMultiset.tailMultiset(c.getElement(), OPEN), c);
382  }
383
384  @CollectionSize.Require(ONE)
385  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
386  public void testSetCountOutOfHeadBoundsOne() {
387    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
388  }
389
390  @CollectionSize.Require(SEVERAL)
391  @CollectionFeature.Require({SUPPORTS_ADD, SUPPORTS_REMOVE})
392  public void testSetCountOutOfHeadBoundsSeveral() {
393    expectSetCountFailure(sortedMultiset.headMultiset(c.getElement(), OPEN), c);
394    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), CLOSED), c);
395    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), c);
396    expectSetCountFailure(sortedMultiset.headMultiset(b.getElement(), OPEN), b);
397    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), c);
398    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), CLOSED), b);
399    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), c);
400    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), b);
401    expectSetCountFailure(sortedMultiset.headMultiset(a.getElement(), OPEN), a);
402  }
403
404  @CollectionSize.Require(SEVERAL)
405  @CollectionFeature.Require(SUPPORTS_ADD)
406  public void testAddWithConflictingBounds() {
407    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(a.getElement(), CLOSED,
408        a.getElement(), OPEN));
409    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(a.getElement(), OPEN,
410        a.getElement(), OPEN));
411    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(a.getElement(), OPEN,
412        a.getElement(), CLOSED));
413    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(b.getElement(), CLOSED,
414        a.getElement(), CLOSED));
415    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(b.getElement(), CLOSED,
416        a.getElement(), OPEN));
417    testEmptyRangeSubMultisetSupportingAdd(sortedMultiset.subMultiset(b.getElement(), OPEN,
418        a.getElement(), OPEN));
419  }
420
421  @CollectionSize.Require(SEVERAL)
422  @CollectionFeature.Require(SUPPORTS_ADD)
423  public void testConflictingBounds() {
424    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(a.getElement(), CLOSED, a.getElement(),
425        OPEN));
426    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(),
427        OPEN));
428    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(a.getElement(), OPEN, a.getElement(),
429        CLOSED));
430    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(),
431        CLOSED));
432    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(b.getElement(), CLOSED, a.getElement(),
433        OPEN));
434    testEmptyRangeSubMultiset(sortedMultiset.subMultiset(b.getElement(), OPEN, a.getElement(),
435        OPEN));
436  }
437
438  public void testEmptyRangeSubMultiset(SortedMultiset<E> multiset) {
439    assertTrue(multiset.isEmpty());
440    assertEquals(0, multiset.size());
441    assertEquals(0, multiset.toArray().length);
442    assertTrue(multiset.entrySet().isEmpty());
443    assertFalse(multiset.iterator().hasNext());
444    assertEquals(0, multiset.entrySet().size());
445    assertEquals(0, multiset.entrySet().toArray().length);
446    assertFalse(multiset.entrySet().iterator().hasNext());
447  }
448
449  @SuppressWarnings("unchecked")
450  public void testEmptyRangeSubMultisetSupportingAdd(SortedMultiset<E> multiset) {
451    for (Entry<E> entry : Arrays.asList(a, b, c)) {
452      expectAddFailure(multiset, entry);
453    }
454  }
455
456  private int totalSize(Iterable<? extends Entry<?>> entries) {
457    int sum = 0;
458    for (Entry<?> entry : entries) {
459      sum += entry.getCount();
460    }
461    return sum;
462  }
463
464  private enum SubMultisetSpec {
465    TAIL_CLOSED {
466      @Override
467      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
468        return entries.subList(targetEntry, entries.size());
469      }
470
471      @Override
472      <E> SortedMultiset<E> subMultiset(SortedMultiset<E> multiset, List<Entry<E>> entries,
473          int targetEntry) {
474        return multiset.tailMultiset(entries.get(targetEntry).getElement(), CLOSED);
475      }
476    },
477    TAIL_OPEN {
478      @Override
479      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
480        return entries.subList(targetEntry + 1, entries.size());
481      }
482
483      @Override
484      <E> SortedMultiset<E> subMultiset(SortedMultiset<E> multiset, List<Entry<E>> entries,
485          int targetEntry) {
486        return multiset.tailMultiset(entries.get(targetEntry).getElement(), OPEN);
487      }
488    },
489    HEAD_CLOSED {
490      @Override
491      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
492        return entries.subList(0, targetEntry + 1);
493      }
494
495      @Override
496      <E> SortedMultiset<E> subMultiset(SortedMultiset<E> multiset, List<Entry<E>> entries,
497          int targetEntry) {
498        return multiset.headMultiset(entries.get(targetEntry).getElement(), CLOSED);
499      }
500    },
501    HEAD_OPEN {
502      @Override
503      <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries) {
504        return entries.subList(0, targetEntry);
505      }
506
507      @Override
508      <E> SortedMultiset<E> subMultiset(SortedMultiset<E> multiset, List<Entry<E>> entries,
509          int targetEntry) {
510        return multiset.headMultiset(entries.get(targetEntry).getElement(), OPEN);
511      }
512    };
513    abstract <E> List<Entry<E>> expectedEntries(int targetEntry, List<Entry<E>> entries);
514
515    abstract <E> SortedMultiset<E> subMultiset(SortedMultiset<E> multiset, List<Entry<E>> entries,
516        int targetEntry);
517  }
518
519  private void testSubMultisetEntrySet(SubMultisetSpec spec) {
520    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
521    for (int i = 0; i < entries.size(); i++) {
522      List<Entry<E>> expected = spec.expectedEntries(i, entries);
523      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
524      assertEquals(expected, copyToList(subMultiset.entrySet()));
525    }
526  }
527
528  private void testSubMultisetSize(SubMultisetSpec spec) {
529    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
530    for (int i = 0; i < entries.size(); i++) {
531      List<Entry<E>> expected = spec.expectedEntries(i, entries);
532      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
533      assertEquals(totalSize(expected), subMultiset.size());
534    }
535  }
536
537  private void testSubMultisetDistinctElements(SubMultisetSpec spec) {
538    List<Entry<E>> entries = copyToList(sortedMultiset.entrySet());
539    for (int i = 0; i < entries.size(); i++) {
540      List<Entry<E>> expected = spec.expectedEntries(i, entries);
541      SortedMultiset<E> subMultiset = spec.subMultiset(sortedMultiset, entries, i);
542      assertEquals(expected.size(), subMultiset.entrySet().size());
543      assertEquals(expected.size(), subMultiset.elementSet().size());
544    }
545  }
546
547  public void testTailClosedEntrySet() {
548    testSubMultisetEntrySet(SubMultisetSpec.TAIL_CLOSED);
549  }
550
551  public void testTailClosedSize() {
552    testSubMultisetSize(SubMultisetSpec.TAIL_CLOSED);
553  }
554
555  public void testTailClosedDistinctElements() {
556    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_CLOSED);
557  }
558
559  public void testTailOpenEntrySet() {
560    testSubMultisetEntrySet(SubMultisetSpec.TAIL_OPEN);
561  }
562
563  public void testTailOpenSize() {
564    testSubMultisetSize(SubMultisetSpec.TAIL_OPEN);
565  }
566
567  public void testTailOpenDistinctElements() {
568    testSubMultisetDistinctElements(SubMultisetSpec.TAIL_OPEN);
569  }
570
571  public void testHeadClosedEntrySet() {
572    testSubMultisetEntrySet(SubMultisetSpec.HEAD_CLOSED);
573  }
574
575  public void testHeadClosedSize() {
576    testSubMultisetSize(SubMultisetSpec.HEAD_CLOSED);
577  }
578
579  public void testHeadClosedDistinctElements() {
580    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_CLOSED);
581  }
582
583  public void testHeadOpenEntrySet() {
584    testSubMultisetEntrySet(SubMultisetSpec.HEAD_OPEN);
585  }
586
587  public void testHeadOpenSize() {
588    testSubMultisetSize(SubMultisetSpec.HEAD_OPEN);
589  }
590
591  public void testHeadOpenDistinctElements() {
592    testSubMultisetDistinctElements(SubMultisetSpec.HEAD_OPEN);
593  }
594
595  @CollectionSize.Require(SEVERAL)
596  @CollectionFeature.Require(SUPPORTS_REMOVE)
597  public void testClearTailOpen() {
598    List<Entry<E>> expected =
599        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
600    sortedMultiset.tailMultiset(b.getElement(), OPEN).clear();
601    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
602  }
603
604  @CollectionSize.Require(SEVERAL)
605  @CollectionFeature.Require(SUPPORTS_REMOVE)
606  public void testClearTailOpenEntrySet() {
607    List<Entry<E>> expected =
608        copyToList(sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet());
609    sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet().clear();
610    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
611  }
612
613  @CollectionSize.Require(SEVERAL)
614  @CollectionFeature.Require(SUPPORTS_REMOVE)
615  public void testClearTailClosed() {
616    List<Entry<E>> expected =
617        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
618    sortedMultiset.tailMultiset(b.getElement(), CLOSED).clear();
619    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
620  }
621
622  @CollectionSize.Require(SEVERAL)
623  @CollectionFeature.Require(SUPPORTS_REMOVE)
624  public void testClearTailClosedEntrySet() {
625    List<Entry<E>> expected =
626        copyToList(sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet());
627    sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet().clear();
628    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
629  }
630
631  @CollectionSize.Require(SEVERAL)
632  @CollectionFeature.Require(SUPPORTS_REMOVE)
633  public void testClearHeadOpen() {
634    List<Entry<E>> expected =
635        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
636    sortedMultiset.headMultiset(b.getElement(), OPEN).clear();
637    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
638  }
639
640  @CollectionSize.Require(SEVERAL)
641  @CollectionFeature.Require(SUPPORTS_REMOVE)
642  public void testClearHeadOpenEntrySet() {
643    List<Entry<E>> expected =
644        copyToList(sortedMultiset.tailMultiset(b.getElement(), CLOSED).entrySet());
645    sortedMultiset.headMultiset(b.getElement(), OPEN).entrySet().clear();
646    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
647  }
648
649  @CollectionSize.Require(SEVERAL)
650  @CollectionFeature.Require(SUPPORTS_REMOVE)
651  public void testClearHeadClosed() {
652    List<Entry<E>> expected =
653        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
654    sortedMultiset.headMultiset(b.getElement(), CLOSED).clear();
655    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
656  }
657
658  @CollectionSize.Require(SEVERAL)
659  @CollectionFeature.Require(SUPPORTS_REMOVE)
660  public void testClearHeadClosedEntrySet() {
661    List<Entry<E>> expected =
662        copyToList(sortedMultiset.tailMultiset(b.getElement(), OPEN).entrySet());
663    sortedMultiset.headMultiset(b.getElement(), CLOSED).entrySet().clear();
664    assertEquals(expected, copyToList(sortedMultiset.entrySet()));
665  }
666}
667