001/*
002 * Copyright (C) 2009 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect.testing;
018
019import static com.google.common.collect.testing.Helpers.entryComparator;
020import static java.lang.Math.max;
021import static java.util.Arrays.asList;
022import static java.util.Collections.singletonMap;
023import static java.util.Collections.sort;
024import static junit.framework.Assert.assertEquals;
025import static junit.framework.Assert.assertFalse;
026import static junit.framework.Assert.assertTrue;
027import static junit.framework.Assert.fail;
028
029import com.google.common.annotations.GwtCompatible;
030import com.google.common.annotations.GwtIncompatible;
031import com.google.common.annotations.J2ktIncompatible;
032import com.google.errorprone.annotations.CanIgnoreReturnValue;
033import java.io.Serializable;
034import java.lang.reflect.Method;
035import java.util.AbstractList;
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.Comparator;
040import java.util.Iterator;
041import java.util.LinkedHashSet;
042import java.util.List;
043import java.util.ListIterator;
044import java.util.Map;
045import java.util.Map.Entry;
046import java.util.Set;
047import org.jspecify.annotations.NullMarked;
048import org.checkerframework.checker.nullness.qual.Nullable;
049
050@GwtCompatible(emulated = true)
051@NullMarked
052public class Helpers {
053  // Clone of Objects.equal
054  static boolean equal(@Nullable Object a, @Nullable Object b) {
055    return a == b || (a != null && a.equals(b));
056  }
057
058  // Clone of Lists.newArrayList
059  public static <E extends @Nullable Object> List<E> copyToList(Iterable<? extends E> elements) {
060    List<E> list = new ArrayList<>();
061    addAll(list, elements);
062    return list;
063  }
064
065  public static <E extends @Nullable Object> List<E> copyToList(E[] elements) {
066    return copyToList(asList(elements));
067  }
068
069  // Clone of Sets.newLinkedHashSet
070  public static <E extends @Nullable Object> Set<E> copyToSet(Iterable<? extends E> elements) {
071    Set<E> set = new LinkedHashSet<>();
072    addAll(set, elements);
073    return set;
074  }
075
076  public static <E extends @Nullable Object> Set<E> copyToSet(E[] elements) {
077    return copyToSet(asList(elements));
078  }
079
080  // Would use Maps.immutableEntry
081  public static <K extends @Nullable Object, V extends @Nullable Object> Entry<K, V> mapEntry(
082      K key, V value) {
083    return singletonMap(key, value).entrySet().iterator().next();
084  }
085
086  private static boolean isEmpty(Iterable<?> iterable) {
087    return iterable instanceof Collection
088        ? ((Collection<?>) iterable).isEmpty()
089        : !iterable.iterator().hasNext();
090  }
091
092  public static void assertEmpty(Iterable<?> iterable) {
093    if (!isEmpty(iterable)) {
094      fail("Not true that " + iterable + " is empty");
095    }
096  }
097
098  public static void assertEmpty(Map<?, ?> map) {
099    if (!map.isEmpty()) {
100      fail("Not true that " + map + " is empty");
101    }
102  }
103
104  public static void assertEqualInOrder(Iterable<?> expected, Iterable<?> actual) {
105    Iterator<?> expectedIter = expected.iterator();
106    Iterator<?> actualIter = actual.iterator();
107
108    while (expectedIter.hasNext() && actualIter.hasNext()) {
109      if (!equal(expectedIter.next(), actualIter.next())) {
110        fail(
111            "contents were not equal and in the same order: "
112                + "expected = "
113                + expected
114                + ", actual = "
115                + actual);
116      }
117    }
118
119    if (expectedIter.hasNext() || actualIter.hasNext()) {
120      // actual either had too few or too many elements
121      fail(
122          "contents were not equal and in the same order: "
123              + "expected = "
124              + expected
125              + ", actual = "
126              + actual);
127    }
128  }
129
130  public static void assertContentsInOrder(Iterable<?> actual, Object... expected) {
131    assertEqualInOrder(asList(expected), actual);
132  }
133
134  public static void assertEqualIgnoringOrder(Iterable<?> expected, Iterable<?> actual) {
135    List<?> exp = copyToList(expected);
136    List<?> act = copyToList(actual);
137    String actString = act.toString();
138
139    // Of course we could take pains to give the complete description of the
140    // problem on any failure.
141
142    // Yeah it's n^2.
143    for (Object object : exp) {
144      if (!act.remove(object)) {
145        fail(
146            "did not contain expected element "
147                + object
148                + ", "
149                + "expected = "
150                + exp
151                + ", actual = "
152                + actString);
153      }
154    }
155    assertTrue("unexpected elements: " + act, act.isEmpty());
156  }
157
158  public static void assertContentsAnyOrder(Iterable<?> actual, Object... expected) {
159    assertEqualIgnoringOrder(asList(expected), actual);
160  }
161
162  public static void assertContains(Iterable<?> actual, Object expected) {
163    boolean contained = false;
164    if (actual instanceof Collection) {
165      contained = ((Collection<?>) actual).contains(expected);
166    } else {
167      for (Object o : actual) {
168        if (equal(o, expected)) {
169          contained = true;
170          break;
171        }
172      }
173    }
174
175    if (!contained) {
176      fail("Not true that " + actual + " contains " + expected);
177    }
178  }
179
180  public static void assertContainsAllOf(Iterable<?> actual, Object... expected) {
181    List<Object> expectedList = new ArrayList<>(asList(expected));
182
183    for (Object o : actual) {
184      expectedList.remove(o);
185    }
186
187    if (!expectedList.isEmpty()) {
188      fail("Not true that " + actual + " contains all of " + asList(expected));
189    }
190  }
191
192  @CanIgnoreReturnValue
193  public static <E extends @Nullable Object> boolean addAll(
194      Collection<E> addTo, Iterable<? extends E> elementsToAdd) {
195    boolean modified = false;
196    for (E e : elementsToAdd) {
197      modified |= addTo.add(e);
198    }
199    return modified;
200  }
201
202  static <T extends @Nullable Object> Iterable<T> reverse(List<T> list) {
203    return new Iterable<T>() {
204      @Override
205      public Iterator<T> iterator() {
206        ListIterator<T> listIter = list.listIterator(list.size());
207        return new Iterator<T>() {
208          @Override
209          public boolean hasNext() {
210            return listIter.hasPrevious();
211          }
212
213          @Override
214          public T next() {
215            return listIter.previous();
216          }
217
218          @Override
219          public void remove() {
220            listIter.remove();
221          }
222        };
223      }
224    };
225  }
226
227  static <T extends @Nullable Object> Iterator<T> cycle(Iterable<T> iterable) {
228    return new Iterator<T>() {
229      Iterator<T> iterator = Collections.<T>emptySet().iterator();
230
231      @Override
232      public boolean hasNext() {
233        return true;
234      }
235
236      @Override
237      public T next() {
238        if (!iterator.hasNext()) {
239          iterator = iterable.iterator();
240        }
241        return iterator.next();
242      }
243
244      @Override
245      public void remove() {
246        throw new UnsupportedOperationException();
247      }
248    };
249  }
250
251  static <T extends @Nullable Object> T get(Iterator<T> iterator, int position) {
252    for (int i = 0; i < position; i++) {
253      iterator.next();
254    }
255    return iterator.next();
256  }
257
258  private static class EntryComparator<K extends @Nullable Object, V extends @Nullable Object>
259      implements Comparator<Entry<K, V>> {
260    final @Nullable Comparator<? super K> keyComparator;
261
262    public EntryComparator(@Nullable Comparator<? super K> keyComparator) {
263      this.keyComparator = keyComparator;
264    }
265
266    @Override
267    @SuppressWarnings("unchecked") // no less safe than putting it in the map!
268    public int compare(Entry<K, V> a, Entry<K, V> b) {
269      return (keyComparator == null)
270          ? ((Comparable) a.getKey()).compareTo(b.getKey())
271          : keyComparator.compare(a.getKey(), b.getKey());
272    }
273  }
274
275  public static <K extends @Nullable Object, V extends @Nullable Object>
276      Comparator<Entry<K, V>> entryComparator(@Nullable Comparator<? super K> keyComparator) {
277    return new EntryComparator<K, V>(keyComparator);
278  }
279
280  /**
281   * Asserts that all pairs of {@code T} values within {@code valuesInExpectedOrder} are ordered
282   * consistently between their order within {@code valuesInExpectedOrder} and the order implied by
283   * the given {@code comparator}.
284   *
285   * @see #testComparator(Comparator, List)
286   */
287  public static <T extends @Nullable Object> void testComparator(
288      Comparator<? super T> comparator, T... valuesInExpectedOrder) {
289    testComparator(comparator, asList(valuesInExpectedOrder));
290  }
291
292  /**
293   * Asserts that all pairs of {@code T} values within {@code valuesInExpectedOrder} are ordered
294   * consistently between their order within {@code valuesInExpectedOrder} and the order implied by
295   * the given {@code comparator}.
296   *
297   * <p>In detail, this method asserts
298   *
299   * <ul>
300   *   <li><i>reflexivity</i>: {@code comparator.compare(t, t) = 0} for all {@code t} in {@code
301   *       valuesInExpectedOrder}; and
302   *   <li><i>consistency</i>: {@code comparator.compare(ti, tj) < 0} and {@code
303   *       comparator.compare(tj, ti) > 0} for {@code i < j}, where {@code ti =
304   *       valuesInExpectedOrder.get(i)} and {@code tj = valuesInExpectedOrder.get(j)}.
305   * </ul>
306   */
307  public static <T extends @Nullable Object> void testComparator(
308      Comparator<? super T> comparator, List<T> valuesInExpectedOrder) {
309    // This does an O(n^2) test of all pairs of values in both orders
310    for (int i = 0; i < valuesInExpectedOrder.size(); i++) {
311      T t = valuesInExpectedOrder.get(i);
312
313      for (int j = 0; j < i; j++) {
314        T lesser = valuesInExpectedOrder.get(j);
315        assertTrue(
316            comparator + ".compare(" + lesser + ", " + t + ")", comparator.compare(lesser, t) < 0);
317      }
318
319      assertEquals(comparator + ".compare(" + t + ", " + t + ")", 0, comparator.compare(t, t));
320
321      for (int j = i + 1; j < valuesInExpectedOrder.size(); j++) {
322        T greater = valuesInExpectedOrder.get(j);
323        assertTrue(
324            comparator + ".compare(" + greater + ", " + t + ")",
325            comparator.compare(greater, t) > 0);
326      }
327    }
328  }
329
330  @SuppressWarnings({"SelfComparison", "SelfEquals"})
331  public static <T extends Comparable<? super T>> void testCompareToAndEquals(
332      List<T> valuesInExpectedOrder) {
333    // This does an O(n^2) test of all pairs of values in both orders
334    for (int i = 0; i < valuesInExpectedOrder.size(); i++) {
335      T t = valuesInExpectedOrder.get(i);
336
337      for (int j = 0; j < i; j++) {
338        T lesser = valuesInExpectedOrder.get(j);
339        assertTrue(lesser + ".compareTo(" + t + ')', lesser.compareTo(t) < 0);
340        assertFalse(lesser.equals(t));
341      }
342
343      assertEquals(t + ".compareTo(" + t + ')', 0, t.compareTo(t));
344      assertTrue(t.equals(t));
345
346      for (int j = i + 1; j < valuesInExpectedOrder.size(); j++) {
347        T greater = valuesInExpectedOrder.get(j);
348        assertTrue(greater + ".compareTo(" + t + ')', greater.compareTo(t) > 0);
349        assertFalse(greater.equals(t));
350      }
351    }
352  }
353
354  /**
355   * Returns a collection that simulates concurrent modification by having its size method return
356   * incorrect values. This is useful for testing methods that must treat the return value from
357   * size() as a hint only.
358   *
359   * @param delta the difference between the true size of the collection and the values returned by
360   *     the size method
361   */
362  public static <T extends @Nullable Object> Collection<T> misleadingSizeCollection(int delta) {
363    // It would be nice to be able to return a real concurrent
364    // collection like ConcurrentLinkedQueue, so that e.g. concurrent
365    // iteration would work, but that would not be GWT-compatible.
366    // We are not "just" inheriting from ArrayList here as this doesn't work for J2kt.
367    return new AbstractList<T>() {
368      ArrayList<T> data = new ArrayList<>();
369
370      @Override
371      public int size() {
372        return max(0, data.size() + delta);
373      }
374
375      @Override
376      public T get(int index) {
377        return data.get(index);
378      }
379
380      @Override
381      public T set(int index, T element) {
382        return data.set(index, element);
383      }
384
385      @Override
386      public boolean add(T element) {
387        return data.add(element);
388      }
389
390      @Override
391      public void add(int index, T element) {
392        data.add(index, element);
393      }
394
395      @Override
396      public T remove(int index) {
397        return data.remove(index);
398      }
399
400      @Override
401      public @Nullable Object[] toArray() {
402        return data.toArray();
403      }
404    };
405  }
406
407  /**
408   * Returns a "nefarious" map entry with the specified key and value, meaning an entry that is
409   * suitable for testing that map entries cannot be modified via a nefarious implementation of
410   * equals. This is used for testing unmodifiable collections of map entries; for example, it
411   * should not be possible to access the raw (modifiable) map entry via a nefarious equals method.
412   */
413  public static <K extends @Nullable Object, V extends @Nullable Object>
414      Entry<K, V> nefariousMapEntry(K key, V value) {
415    return new Entry<K, V>() {
416      @Override
417      public K getKey() {
418        return key;
419      }
420
421      @Override
422      public V getValue() {
423        return value;
424      }
425
426      @Override
427      public V setValue(V value) {
428        throw new UnsupportedOperationException();
429      }
430
431      @SuppressWarnings("unchecked")
432      @Override
433      public boolean equals(@Nullable Object o) {
434        if (o instanceof Entry) {
435          Entry<K, V> e = (Entry<K, V>) o;
436          e.setValue(value); // muhahaha!
437
438          return equal(this.getKey(), e.getKey()) && equal(this.getValue(), e.getValue());
439        }
440        return false;
441      }
442
443      @Override
444      public int hashCode() {
445        K k = getKey();
446        V v = getValue();
447        return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
448      }
449
450      @Override
451      public String toString() {
452        return getKey() + "=" + getValue();
453      }
454    };
455  }
456
457  static <E extends @Nullable Object> List<E> castOrCopyToList(Iterable<E> iterable) {
458    if (iterable instanceof List) {
459      return (List<E>) iterable;
460    }
461    List<E> list = new ArrayList<>();
462    for (E e : iterable) {
463      list.add(e);
464    }
465    return list;
466  }
467
468  @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989
469  public static <K extends Comparable, V extends @Nullable Object>
470      Iterable<Entry<K, V>> orderEntriesByKey(List<Entry<K, V>> insertionOrder) {
471    @SuppressWarnings("unchecked") // assume any Comparable is Comparable<Self>
472    Comparator<? super K> keyComparator = (Comparator<? super K>) Comparable::compareTo;
473    sort(insertionOrder, entryComparator(keyComparator));
474    return insertionOrder;
475  }
476
477  /**
478   * Private replacement for {@link com.google.gwt.user.client.rpc.GwtTransient} to work around
479   * build-system quirks.
480   */
481  private @interface GwtTransient {}
482
483  /**
484   * Compares strings in natural order except that null comes immediately before a given value. This
485   * works better than Ordering.natural().nullsFirst() because, if null comes before all other
486   * values, it lies outside the submap/submultiset ranges we test, and the variety of tests that
487   * exercise null handling fail on those subcollections.
488   */
489  public abstract static class NullsBefore implements Comparator<@Nullable String>, Serializable {
490    /*
491     * We don't serialize this class in GWT, so we don't care about whether GWT will serialize this
492     * field.
493     */
494    @GwtTransient private final String justAfterNull;
495
496    protected NullsBefore(String justAfterNull) {
497      if (justAfterNull == null) {
498        throw new NullPointerException();
499      }
500
501      this.justAfterNull = justAfterNull;
502    }
503
504    @Override
505    public int compare(@Nullable String lhs, @Nullable String rhs) {
506      if (lhs == rhs) {
507        return 0;
508      }
509      if (lhs == null) {
510        // lhs (null) comes just before justAfterNull.
511        // If rhs is b, lhs comes first.
512        if (rhs.equals(justAfterNull)) {
513          return -1;
514        }
515        return justAfterNull.compareTo(rhs);
516      }
517      if (rhs == null) {
518        // rhs (null) comes just before justAfterNull.
519        // If lhs is b, rhs comes first.
520        if (lhs.equals(justAfterNull)) {
521          return 1;
522        }
523        return lhs.compareTo(justAfterNull);
524      }
525      return lhs.compareTo(rhs);
526    }
527
528    @Override
529    public boolean equals(@Nullable Object obj) {
530      if (obj instanceof NullsBefore) {
531        NullsBefore other = (NullsBefore) obj;
532        return justAfterNull.equals(other.justAfterNull);
533      }
534      return false;
535    }
536
537    @Override
538    public int hashCode() {
539      return justAfterNull.hashCode();
540    }
541  }
542
543  public static final class NullsBeforeB extends NullsBefore {
544    public static final NullsBeforeB INSTANCE = new NullsBeforeB();
545
546    private NullsBeforeB() {
547      super("b");
548    }
549  }
550
551  public static final class NullsBeforeTwo extends NullsBefore {
552    public static final NullsBeforeTwo INSTANCE = new NullsBeforeTwo();
553
554    private NullsBeforeTwo() {
555      super("two"); // from TestStringSortedMapGenerator's sample keys
556    }
557  }
558
559  @J2ktIncompatible
560  @GwtIncompatible // reflection
561  public static Method getMethod(Class<?> clazz, String name) {
562    try {
563      return clazz.getMethod(name);
564    } catch (Exception e) {
565      throw new IllegalArgumentException(e);
566    }
567  }
568}