1/*
2 * Copyright (C) 2009 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.testing;
18
19import static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertFalse;
21import static junit.framework.Assert.assertTrue;
22
23import junit.framework.Assert;
24import junit.framework.AssertionFailedError;
25
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.Comparator;
31import java.util.Iterator;
32import java.util.LinkedHashSet;
33import java.util.List;
34import java.util.ListIterator;
35import java.util.Map;
36import java.util.Map.Entry;
37import java.util.Set;
38
39// This class is GWT compatible.
40public class Helpers {
41  // Clone of Objects.equal
42  static boolean equal(Object a, Object b) {
43    return a == b || (a != null && a.equals(b));
44  }
45
46  // Clone of Lists.newArrayList
47  public static <E> List<E> copyToList(Iterable<? extends E> elements) {
48    List<E> list = new ArrayList<E>();
49    addAll(list, elements);
50    return list;
51  }
52
53  public static <E> List<E> copyToList(E[] elements) {
54    return copyToList(Arrays.asList(elements));
55  }
56
57  // Clone of Sets.newLinkedHashSet
58  public static <E> Set<E> copyToSet(Iterable<? extends E> elements) {
59    Set<E> set = new LinkedHashSet<E>();
60    addAll(set, elements);
61    return set;
62  }
63
64  public static <E> Set<E> copyToSet(E[] elements) {
65    return copyToSet(Arrays.asList(elements));
66  }
67
68  // Would use Maps.immutableEntry
69  static <K, V> Entry<K, V> mapEntry(K key, V value) {
70    return Collections.singletonMap(key, value).entrySet().iterator().next();
71  }
72
73  public static void assertEqualIgnoringOrder(
74      Iterable<?> expected, Iterable<?> actual) {
75    List<?> exp = copyToList(expected);
76    List<?> act = copyToList(actual);
77    String actString = act.toString();
78
79    // Of course we could take pains to give the complete description of the
80    // problem on any failure.
81
82    // Yeah it's n^2.
83    for (Object object : exp) {
84      if (!act.remove(object)) {
85        Assert.fail("did not contain expected element " + object + ", "
86            + "expected = " + exp + ", actual = " + actString);
87      }
88    }
89    assertTrue("unexpected elements: " + act, act.isEmpty());
90  }
91
92  public static void assertContentsAnyOrder(
93      Iterable<?> actual, Object... expected) {
94    assertEqualIgnoringOrder(Arrays.asList(expected), actual);
95  }
96
97  public static <E> boolean addAll(
98      Collection<E> addTo, Iterable<? extends E> elementsToAdd) {
99    boolean modified = false;
100    for (E e : elementsToAdd) {
101      modified |= addTo.add(e);
102    }
103    return modified;
104  }
105
106  static <T> Iterable<T> reverse(final List<T> list) {
107    return new Iterable<T>() {
108      @Override
109      public Iterator<T> iterator() {
110        final ListIterator<T> listIter = list.listIterator(list.size());
111        return new Iterator<T>() {
112          @Override
113          public boolean hasNext() {
114            return listIter.hasPrevious();
115          }
116          @Override
117          public T next() {
118            return listIter.previous();
119          }
120          @Override
121          public void remove() {
122            listIter.remove();
123          }
124        };
125      }
126    };
127  }
128
129  static <T> Iterator<T> cycle(final Iterable<T> iterable) {
130    return new Iterator<T>() {
131      Iterator<T> iterator = Collections.<T>emptySet().iterator();
132      @Override
133      public boolean hasNext() {
134        return true;
135      }
136      @Override
137      public T next() {
138        if (!iterator.hasNext()) {
139          iterator = iterable.iterator();
140        }
141        return iterator.next();
142      }
143      @Override
144      public void remove() {
145        throw new UnsupportedOperationException();
146      }
147    };
148  }
149
150  static <T> T get(Iterator<T> iterator, int position) {
151    for (int i = 0; i < position; i++) {
152      iterator.next();
153    }
154    return iterator.next();
155  }
156
157  static void fail(Throwable cause, Object message) {
158    AssertionFailedError assertionFailedError =
159        new AssertionFailedError(String.valueOf(message));
160    assertionFailedError.initCause(cause);
161    throw assertionFailedError;
162  }
163
164  public static <K, V> Comparator<Entry<K, V>> entryComparator(
165      final Comparator<? super K> keyComparator) {
166    return new Comparator<Entry<K, V>>() {
167      @Override
168      public int compare(Entry<K, V> a, Entry<K, V> b) {
169        return keyComparator.compare(a.getKey(), b.getKey());
170      }
171    };
172  }
173
174  public static <T> void testComparator(
175      Comparator<? super T> comparator, T... valuesInExpectedOrder) {
176    testComparator(comparator, Arrays.asList(valuesInExpectedOrder));
177  }
178
179  public static <T> void testComparator(
180      Comparator<? super T> comparator, List<T> valuesInExpectedOrder) {
181    // This does an O(n^2) test of all pairs of values in both orders
182    for (int i = 0; i < valuesInExpectedOrder.size(); i++) {
183      T t = valuesInExpectedOrder.get(i);
184
185      for (int j = 0; j < i; j++) {
186        T lesser = valuesInExpectedOrder.get(j);
187        assertTrue(comparator + ".compare(" + lesser + ", " + t + ")",
188            comparator.compare(lesser, t) < 0);
189      }
190
191      assertEquals(comparator + ".compare(" + t + ", " + t + ")",
192          0, comparator.compare(t, t));
193
194      for (int j = i + 1; j < valuesInExpectedOrder.size(); j++) {
195        T greater = valuesInExpectedOrder.get(j);
196        assertTrue(comparator + ".compare(" + greater + ", " + t + ")",
197            comparator.compare(greater, t) > 0);
198      }
199    }
200  }
201
202  public static <T extends Comparable<? super T>> void testCompareToAndEquals(
203      List<T> valuesInExpectedOrder) {
204    // This does an O(n^2) test of all pairs of values in both orders
205    for (int i = 0; i < valuesInExpectedOrder.size(); i++) {
206      T t = valuesInExpectedOrder.get(i);
207
208      for (int j = 0; j < i; j++) {
209        T lesser = valuesInExpectedOrder.get(j);
210        assertTrue(lesser + ".compareTo(" + t + ')', lesser.compareTo(t) < 0);
211        assertFalse(lesser.equals(t));
212      }
213
214      assertEquals(t + ".compareTo(" + t + ')', 0, t.compareTo(t));
215      assertTrue(t.equals(t));
216
217      for (int j = i + 1; j < valuesInExpectedOrder.size(); j++) {
218        T greater = valuesInExpectedOrder.get(j);
219        assertTrue(greater + ".compareTo(" + t + ')', greater.compareTo(t) > 0);
220        assertFalse(greater.equals(t));
221      }
222    }
223  }
224
225  /**
226   * Returns a collection that simulates concurrent modification by
227   * having its size method return incorrect values.  This is useful
228   * for testing methods that must treat the return value from size()
229   * as a hint only.
230   *
231   * @param delta the difference between the true size of the
232   * collection and the values returned by the size method
233   */
234  public static <T> Collection<T> misleadingSizeCollection(final int delta) {
235    // It would be nice to be able to return a real concurrent
236    // collection like ConcurrentLinkedQueue, so that e.g. concurrent
237    // iteration would work, but that would not be GWT-compatible.
238    return new ArrayList<T>() {
239      @Override public int size() { return Math.max(0, super.size() + delta); }
240    };
241  }
242
243  /**
244   * Returns a "nefarious" map entry with the specified key and value,
245   * meaning an entry that is suitable for testing that map entries cannot be
246   * modified via a nefarious implementation of equals. This is used for testing
247   * unmodifiable collections of map entries; for example, it should not be
248   * possible to access the raw (modifiable) map entry via a nefarious equals
249   * method.
250   */
251  public static <K, V> Map.Entry<K, V> nefariousMapEntry(final K key,
252      final V value) {
253    return new Map.Entry<K, V>() {
254      @Override public K getKey() {
255        return key;
256      }
257      @Override public V getValue() {
258        return value;
259      }
260      @Override public V setValue(V value) {
261        throw new UnsupportedOperationException();
262      }
263      @SuppressWarnings("unchecked")
264      @Override public boolean equals(Object o) {
265        if (o instanceof Map.Entry<?, ?>) {
266          Map.Entry<K, V> e = (Map.Entry<K, V>) o;
267          e.setValue(value); // muhahaha!
268
269          return equal(this.getKey(), e.getKey())
270              && equal(this.getValue(), e.getValue());
271        }
272        return false;
273      }
274
275      @Override public int hashCode() {
276        K k = getKey();
277        V v = getValue();
278        return ((k == null) ?
279            0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
280      }
281
282      /**
283       * Returns a string representation of the form <code>{key}={value}</code>.
284       */
285      @Override public String toString() {
286        return getKey() + "=" + getValue();
287      }
288    };
289  }
290}
291