1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static java.util.Arrays.asList;
20import static org.truth0.Truth.ASSERT;
21
22import com.google.common.annotations.GwtCompatible;
23
24import junit.framework.TestCase;
25
26import java.util.AbstractCollection;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Iterator;
30import java.util.List;
31import java.util.ListIterator;
32import java.util.RandomAccess;
33import java.util.Set;
34import java.util.SortedSet;
35
36/**
37 * Tests for {@code Constraints}.
38 *
39 * @author Mike Bostock
40 * @author Jared Levy
41 */
42@GwtCompatible(emulated = true)
43public class ConstraintsTest extends TestCase {
44
45  private static final String TEST_ELEMENT = "test";
46
47  private static final class TestElementException
48      extends IllegalArgumentException {
49    private static final long serialVersionUID = 0;
50  }
51
52  private static final Constraint<String> TEST_CONSTRAINT
53      = new Constraint<String>() {
54          @Override
55          public String checkElement(String element) {
56            if (TEST_ELEMENT.equals(element)) {
57              throw new TestElementException();
58            }
59            return element;
60          }
61        };
62
63  public void testNotNull() {
64    Constraint<? super String> constraint = Constraints.notNull();
65    assertSame(TEST_ELEMENT, constraint.checkElement(TEST_ELEMENT));
66    try {
67      constraint.checkElement(null);
68      fail("NullPointerException expected");
69    } catch (NullPointerException expected) {}
70    assertEquals("Not null", constraint.toString());
71  }
72
73  public void testConstrainedCollectionLegal() {
74    Collection<String> collection = Lists.newArrayList("foo", "bar");
75    Collection<String> constrained = Constraints.constrainedCollection(
76        collection, TEST_CONSTRAINT);
77    collection.add(TEST_ELEMENT);
78    constrained.add("qux");
79    constrained.addAll(asList("cat", "dog"));
80    /* equals and hashCode aren't defined for Collection */
81    ASSERT.that(collection).has()
82        .exactly("foo", "bar", TEST_ELEMENT, "qux", "cat", "dog").inOrder();
83    ASSERT.that(constrained).has()
84        .exactly("foo", "bar", TEST_ELEMENT, "qux", "cat", "dog").inOrder();
85  }
86
87  public void testConstrainedCollectionIllegal() {
88    Collection<String> collection = Lists.newArrayList("foo", "bar");
89    Collection<String> constrained = Constraints.constrainedCollection(
90        collection, TEST_CONSTRAINT);
91    try {
92      constrained.add(TEST_ELEMENT);
93      fail("TestElementException expected");
94    } catch (TestElementException expected) {}
95    try {
96      constrained.addAll(asList("baz", TEST_ELEMENT));
97      fail("TestElementException expected");
98    } catch (TestElementException expected) {}
99    ASSERT.that(constrained).has().exactly("foo", "bar").inOrder();
100    ASSERT.that(collection).has().exactly("foo", "bar").inOrder();
101  }
102
103  public void testConstrainedSetLegal() {
104    Set<String> set = Sets.newLinkedHashSet(asList("foo", "bar"));
105    Set<String> constrained = Constraints.constrainedSet(set, TEST_CONSTRAINT);
106    set.add(TEST_ELEMENT);
107    constrained.add("qux");
108    constrained.addAll(asList("cat", "dog"));
109    assertTrue(set.equals(constrained));
110    assertTrue(constrained.equals(set));
111    assertEquals(set.toString(), constrained.toString());
112    assertEquals(set.hashCode(), constrained.hashCode());
113    ASSERT.that(set).has().exactly("foo", "bar", TEST_ELEMENT, "qux", "cat", "dog").inOrder();
114    ASSERT.that(constrained).has()
115        .exactly("foo", "bar", TEST_ELEMENT, "qux", "cat", "dog").inOrder();
116  }
117
118  public void testConstrainedSetIllegal() {
119    Set<String> set = Sets.newLinkedHashSet(asList("foo", "bar"));
120    Set<String> constrained = Constraints.constrainedSet(set, TEST_CONSTRAINT);
121    try {
122      constrained.add(TEST_ELEMENT);
123      fail("TestElementException expected");
124    } catch (TestElementException expected) {}
125    try {
126      constrained.addAll(asList("baz", TEST_ELEMENT));
127      fail("TestElementException expected");
128    } catch (TestElementException expected) {}
129    ASSERT.that(constrained).has().exactly("foo", "bar").inOrder();
130    ASSERT.that(set).has().exactly("foo", "bar").inOrder();
131  }
132
133  public void testConstrainedSortedSetLegal() {
134    SortedSet<String> sortedSet = Sets.newTreeSet(asList("foo", "bar"));
135    SortedSet<String> constrained = Constraints.constrainedSortedSet(
136        sortedSet, TEST_CONSTRAINT);
137    sortedSet.add(TEST_ELEMENT);
138    constrained.add("qux");
139    constrained.addAll(asList("cat", "dog"));
140    assertTrue(sortedSet.equals(constrained));
141    assertTrue(constrained.equals(sortedSet));
142    assertEquals(sortedSet.toString(), constrained.toString());
143    assertEquals(sortedSet.hashCode(), constrained.hashCode());
144    ASSERT.that(sortedSet).has().exactly("bar", "cat", "dog", "foo", "qux", TEST_ELEMENT).inOrder();
145    ASSERT.that(constrained).has()
146        .exactly("bar", "cat", "dog", "foo", "qux", TEST_ELEMENT).inOrder();
147    assertNull(constrained.comparator());
148    assertEquals("bar", constrained.first());
149    assertEquals(TEST_ELEMENT, constrained.last());
150  }
151
152  public void testConstrainedSortedSetIllegal() {
153    SortedSet<String> sortedSet = Sets.newTreeSet(asList("foo", "bar"));
154    SortedSet<String> constrained = Constraints.constrainedSortedSet(
155        sortedSet, TEST_CONSTRAINT);
156    try {
157      constrained.add(TEST_ELEMENT);
158      fail("TestElementException expected");
159    } catch (TestElementException expected) {}
160    try {
161      constrained.subSet("bar", "foo").add(TEST_ELEMENT);
162      fail("TestElementException expected");
163    } catch (TestElementException expected) {}
164    try {
165      constrained.headSet("bar").add(TEST_ELEMENT);
166      fail("TestElementException expected");
167    } catch (TestElementException expected) {}
168    try {
169      constrained.tailSet("foo").add(TEST_ELEMENT);
170      fail("TestElementException expected");
171    } catch (TestElementException expected) {}
172    try {
173      constrained.addAll(asList("baz", TEST_ELEMENT));
174      fail("TestElementException expected");
175    } catch (TestElementException expected) {}
176    ASSERT.that(constrained).has().exactly("bar", "foo").inOrder();
177    ASSERT.that(sortedSet).has().exactly("bar", "foo").inOrder();
178  }
179
180  public void testConstrainedListLegal() {
181    List<String> list = Lists.newArrayList("foo", "bar");
182    List<String> constrained = Constraints.constrainedList(
183        list, TEST_CONSTRAINT);
184    list.add(TEST_ELEMENT);
185    constrained.add("qux");
186    constrained.addAll(asList("cat", "dog"));
187    constrained.add(1, "cow");
188    constrained.addAll(4, asList("box", "fan"));
189    constrained.set(2, "baz");
190    assertTrue(list.equals(constrained));
191    assertTrue(constrained.equals(list));
192    assertEquals(list.toString(), constrained.toString());
193    assertEquals(list.hashCode(), constrained.hashCode());
194    ASSERT.that(list).has().exactly(
195        "foo", "cow", "baz", TEST_ELEMENT, "box", "fan", "qux", "cat", "dog").inOrder();
196    ASSERT.that(constrained).has().exactly(
197        "foo", "cow", "baz", TEST_ELEMENT, "box", "fan", "qux", "cat", "dog").inOrder();
198    ListIterator<String> iterator = constrained.listIterator();
199    iterator.next();
200    iterator.set("sun");
201    constrained.listIterator(2).add("sky");
202    ASSERT.that(list).has().exactly(
203        "sun", "cow", "sky", "baz", TEST_ELEMENT, "box", "fan", "qux", "cat", "dog").inOrder();
204    ASSERT.that(constrained).has().exactly(
205        "sun", "cow", "sky", "baz", TEST_ELEMENT, "box", "fan", "qux", "cat", "dog").inOrder();
206    assertTrue(constrained instanceof RandomAccess);
207  }
208
209  public void testConstrainedListRandomAccessFalse() {
210    List<String> list = Lists.newLinkedList(asList("foo", "bar"));
211    List<String> constrained = Constraints.constrainedList(
212        list, TEST_CONSTRAINT);
213    list.add(TEST_ELEMENT);
214    constrained.add("qux");
215    assertFalse(constrained instanceof RandomAccess);
216  }
217
218  public void testConstrainedListIllegal() {
219    List<String> list = Lists.newArrayList("foo", "bar");
220    List<String> constrained = Constraints.constrainedList(
221        list, TEST_CONSTRAINT);
222    try {
223      constrained.add(TEST_ELEMENT);
224      fail("TestElementException expected");
225    } catch (TestElementException expected) {}
226    try {
227      constrained.listIterator().add(TEST_ELEMENT);
228      fail("TestElementException expected");
229    } catch (TestElementException expected) {}
230    try {
231      constrained.listIterator(1).add(TEST_ELEMENT);
232      fail("TestElementException expected");
233    } catch (TestElementException expected) {}
234    try {
235      constrained.listIterator().set(TEST_ELEMENT);
236      fail("TestElementException expected");
237    } catch (TestElementException expected) {}
238    try {
239      constrained.listIterator(1).set(TEST_ELEMENT);
240      fail("TestElementException expected");
241    } catch (TestElementException expected) {}
242    try {
243      constrained.subList(0, 1).add(TEST_ELEMENT);
244      fail("TestElementException expected");
245    } catch (TestElementException expected) {}
246    try {
247      constrained.add(1, TEST_ELEMENT);
248      fail("TestElementException expected");
249    } catch (TestElementException expected) {}
250    try {
251      constrained.set(1, TEST_ELEMENT);
252      fail("TestElementException expected");
253    } catch (TestElementException expected) {}
254    try {
255      constrained.addAll(asList("baz", TEST_ELEMENT));
256      fail("TestElementException expected");
257    } catch (TestElementException expected) {}
258    try {
259      constrained.addAll(1, asList("baz", TEST_ELEMENT));
260      fail("TestElementException expected");
261    } catch (TestElementException expected) {}
262    ASSERT.that(constrained).has().exactly("foo", "bar").inOrder();
263    ASSERT.that(list).has().exactly("foo", "bar").inOrder();
264  }
265
266  public void testConstrainedMultisetLegal() {
267    Multiset<String> multiset = HashMultiset.create(asList("foo", "bar"));
268    Multiset<String> constrained = Constraints.constrainedMultiset(
269        multiset, TEST_CONSTRAINT);
270    multiset.add(TEST_ELEMENT);
271    constrained.add("qux");
272    constrained.addAll(asList("cat", "dog"));
273    constrained.add("cow", 2);
274    assertTrue(multiset.equals(constrained));
275    assertTrue(constrained.equals(multiset));
276    assertEquals(multiset.toString(), constrained.toString());
277    assertEquals(multiset.hashCode(), constrained.hashCode());
278    ASSERT.that(multiset).has().exactly(
279        "foo", "bar", TEST_ELEMENT, "qux", "cat", "dog", "cow", "cow");
280    ASSERT.that(constrained).has().exactly(
281        "foo", "bar", TEST_ELEMENT, "qux", "cat", "dog", "cow", "cow");
282    assertEquals(1, constrained.count("foo"));
283    assertEquals(1, constrained.remove("foo", 3));
284    assertEquals(2, constrained.setCount("cow", 0));
285    ASSERT.that(multiset).has().exactly("bar", TEST_ELEMENT, "qux", "cat", "dog");
286    ASSERT.that(constrained).has().exactly("bar", TEST_ELEMENT, "qux", "cat", "dog");
287  }
288
289  public void testConstrainedMultisetIllegal() {
290    Multiset<String> multiset = HashMultiset.create(asList("foo", "bar"));
291    Multiset<String> constrained = Constraints.constrainedMultiset(
292        multiset, TEST_CONSTRAINT);
293    try {
294      constrained.add(TEST_ELEMENT);
295      fail("TestElementException expected");
296    } catch (TestElementException expected) {}
297    try {
298      constrained.add(TEST_ELEMENT, 2);
299      fail("TestElementException expected");
300    } catch (TestElementException expected) {}
301    try {
302      constrained.addAll(asList("baz", TEST_ELEMENT));
303      fail("TestElementException expected");
304    } catch (TestElementException expected) {}
305    ASSERT.that(constrained).has().exactly("foo", "bar");
306    ASSERT.that(multiset).has().exactly("foo", "bar");
307  }
308
309  public void testNefariousAddAll() {
310    List<String> list = Lists.newArrayList("foo", "bar");
311    List<String> constrained = Constraints.constrainedList(
312        list, TEST_CONSTRAINT);
313    Collection<String> onceIterable = onceIterableCollection("baz");
314    constrained.addAll(onceIterable);
315    ASSERT.that(constrained).has().exactly("foo", "bar", "baz").inOrder();
316    ASSERT.that(list).has().exactly("foo", "bar", "baz").inOrder();
317  }
318
319  /**
320   * Returns a "nefarious" collection, which permits only one call to
321   * iterator(). This verifies that the constrained collection uses a defensive
322   * copy instead of potentially checking the elements in one snapshot and
323   * adding the elements from another.
324   *
325   * @param element the element to be contained in the collection
326   */
327  static <E> Collection<E> onceIterableCollection(final E element) {
328    return new AbstractCollection<E>() {
329      boolean iteratorCalled;
330      @Override public int size() {
331        /*
332         * We could make the collection empty, but that seems more likely to
333         * trigger special cases (so maybe we should test both empty and
334         * nonempty...).
335         */
336        return 1;
337      }
338      @Override public Iterator<E> iterator() {
339        assertFalse("Expected only one call to iterator()", iteratorCalled);
340        iteratorCalled = true;
341        return Collections.singleton(element).iterator();
342      }
343    };
344  }
345}
346
347