All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.testing.FreshValueGenerator Maven / Gradle / Ivy

Go to download

Guava testlib is a set of java classes used for more convenient unit testing - particularly to assist the tests for Guava itself.

There is a newer version: 17.0
Show newest version
/*
 * Copyright (C) 2012 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.testing;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.CharMatcher;
import com.google.common.base.Charsets;
import com.google.common.base.Equivalence;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.base.Ticker;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedMultiset;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedHashMultiset;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Ordering;
import com.google.common.collect.RowSortedTable;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedMultiset;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.TreeMultiset;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import com.google.common.reflect.AbstractInvocationHandler;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Parameter;
import com.google.common.reflect.Reflection;
import com.google.common.reflect.TypeToken;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Currency;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

/**
 * Generates fresh instances of types that are different from each other (if possible).
 *
 * @author Ben Yu
 */
class FreshValueGenerator {

  private static final ImmutableMap, Method> GENERATORS;
  static {
    ImmutableMap.Builder, Method> builder =
        ImmutableMap.builder();
    for (Method method : FreshValueGenerator.class.getDeclaredMethods()) {
      if (method.isAnnotationPresent(Generates.class)) {
        builder.put(method.getReturnType(), method);
      }
    }
    GENERATORS = builder.build();
  }

  private final AtomicInteger differentiator = new AtomicInteger(1);
  private final ListMultimap, Object> sampleInstances = ArrayListMultimap.create();

   void addSampleInstances(Class type, Iterable instances) {
    sampleInstances.putAll(checkNotNull(type), checkNotNull(instances));
  }

  /**
   * Returns a fresh instance for {@code type} if possible. The returned instance could be:
   * 
    *
  • exactly of the given type, including generic type parameters, such as * {@code ImmutableList}; *
  • of the raw type; *
  • null if no fresh value can be generated. *
*/ @Nullable final T generate(TypeToken type) { // Not completely safe since sample instances are registered by raw types. // But we assume the generic type parameters are mostly unimportant for these dummy values, // because what really matters are equals/hashCode. @SuppressWarnings("unchecked") T result = (T) generateIfPossible(type); return result; } @Nullable final T generate(Class type) { return generate(TypeToken.of(type)); } @Nullable private Object generateIfPossible(TypeToken type) { Class rawType = type.getRawType(); List samples = sampleInstances.get(rawType); Object sample = nextInstance(samples, null); if (sample != null) { return sample; } if (rawType.isEnum()) { return nextInstance(rawType.getEnumConstants(), null); } if (type.isArray()) { TypeToken componentType = type.getComponentType(); Object array = Array.newInstance(componentType.getRawType(), 1); Array.set(array, 0, generateIfPossible(componentType)); return array; } Method generator = GENERATORS.get(rawType); if (generator != null) { ImmutableList params = Invokable.from(generator).getParameters(); List args = Lists.newArrayListWithCapacity(params.size()); TypeVariable[] typeVars = rawType.getTypeParameters(); for (int i = 0; i < params.size(); i++) { TypeToken paramType = type.resolveType(typeVars[i]); // We require all @Generates methods to either be parameter-less or accept non-null // fresh values for their generic parameter types. Object argValue = generateIfPossible(paramType); if (argValue == null) { return defaultGenerate(rawType); } args.add(argValue); } try { return generator.invoke(this, args.toArray()); } catch (InvocationTargetException e) { Throwables.propagate(e.getCause()); } catch (Exception e) { throw Throwables.propagate(e); } } return defaultGenerate(rawType); } private Object defaultGenerate(Class rawType) { if (rawType.isInterface()) { // always create a new proxy return newProxy(rawType); } return ArbitraryInstances.get(rawType); } final T newProxy(final Class interfaceType) { return Reflection.newProxy(interfaceType, new FreshInvocationHandler(interfaceType)); } private final class FreshInvocationHandler extends AbstractInvocationHandler { private final int identity = freshInt(); private final Class interfaceType; FreshInvocationHandler(Class interfaceType) { this.interfaceType = interfaceType; } @Override protected Object handleInvocation(Object proxy, Method method, Object[] args) { return interfaceMethodCalled(interfaceType, method); } @Override public int hashCode() { return identity; } @Override public boolean equals(@Nullable Object obj) { if (obj instanceof FreshInvocationHandler) { FreshInvocationHandler that = (FreshInvocationHandler) obj; return identity == that.identity; } return false; } @Override public String toString() { return paramString(interfaceType, identity); } } /** Subclasses can override to provide different return value for proxied interface methods. */ Object interfaceMethodCalled( @SuppressWarnings("unused") Class interfaceType, @SuppressWarnings("unused") Method method) { throw new UnsupportedOperationException(); } private T nextInstance(T[] instances, T defaultValue) { return nextInstance(Arrays.asList(instances), defaultValue); } private T nextInstance(Collection instances, T defaultValue) { if (instances.isEmpty()) { return defaultValue; } // freshInt() is 1-based. return Iterables.get(instances, (freshInt() - 1) % instances.size()); } private static String paramString(Class type, int i) { return type.getSimpleName() + '@' + i; } /** * Annotates a method to be the instance generator of a certain type. The return type is the * generated type. The method parameters are non-null fresh values for each method type variable * in the same type variable declaration order of the return type. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) private @interface Generates {} @Generates private Class freshClass() { return nextInstance( ImmutableList.of( int.class, long.class, void.class, Object.class, Object[].class, Iterable.class), Object.class); } @Generates private Object freshObject() { return freshString(); } @Generates private Number freshNumber() { return freshInt(); } @Generates private int freshInt() { return differentiator.getAndIncrement(); } @Generates private Integer freshInteger() { return new Integer(freshInt()); } @Generates private long freshLong() { return freshInt(); } @Generates private Long freshLongObject() { return new Long(freshLong()); } @Generates private float freshFloat() { return freshInt(); } @Generates private Float freshFloatObject() { return new Float(freshFloat()); } @Generates private double freshDouble() { return freshInt(); } @Generates private Double freshDoubleObject() { return new Double(freshDouble()); } @Generates private short freshShort() { return (short) freshInt(); } @Generates private Short freshShortObject() { return new Short(freshShort()); } @Generates private byte freshByte() { return (byte) freshInt(); } @Generates private Byte freshByteObject() { return new Byte(freshByte()); } @Generates private char freshChar() { return freshString().charAt(0); } @Generates private Character freshCharacter() { return new Character(freshChar()); } @Generates private boolean freshBoolean() { return freshInt() % 2 == 0; } @Generates private Boolean freshBooleanObject() { return new Boolean(freshBoolean()); } @Generates private UnsignedInteger freshUnsignedInteger() { return UnsignedInteger.fromIntBits(freshInt()); } @Generates private UnsignedLong freshUnsignedLong() { return UnsignedLong.fromLongBits(freshLong()); } @Generates private BigInteger freshBigInteger() { return BigInteger.valueOf(freshInt()); } @Generates private BigDecimal freshBigDecimal() { return BigDecimal.valueOf(freshInt()); } @Generates private CharSequence freshCharSequence() { return freshString(); } @Generates private String freshString() { return Integer.toString(freshInt()); } @Generates private Comparable freshComparable() { return freshString(); } @Generates private Pattern freshPattern() { return Pattern.compile(freshString()); } @Generates private Charset freshCharset() { return nextInstance(Charset.availableCharsets().values(), Charsets.UTF_8); } @Generates private Locale freshLocale() { return nextInstance(Locale.getAvailableLocales(), Locale.US); } @Generates private Currency freshCurrency() { for (Set uselessLocales = Sets.newHashSet(); ; ) { Locale locale = freshLocale(); if (uselessLocales.contains(locale)) { // exhausted all locales return Currency.getInstance(Locale.US); } try { return Currency.getInstance(locale); } catch (IllegalArgumentException e) { uselessLocales.add(locale); } } } // common.base @Generates private Joiner freshJoiner() { return Joiner.on(freshString()); } @Generates private Splitter freshSplitter() { return Splitter.on(freshString()); } @Generates private Equivalence freshEquivalence() { return new Equivalence() { @Override protected boolean doEquivalent(T a, T b) { return false; } @Override protected int doHash(T t) { return 0; } final String string = paramString(Equivalence.class, freshInt()); @Override public String toString() { return string; } }; } @Generates private CharMatcher freshCharMatcher() { return new CharMatcher() { @Override public boolean matches(char c) { return false; } final String string = paramString(CharMatcher.class, freshInt()); @Override public String toString() { return string; } }; } @Generates private Ticker freshTicker() { return new Ticker() { @Override public long read() { return 0; } final String string = paramString(Ticker.class, freshInt()); @Override public String toString() { return string; } }; } // collect @Generates private Comparator freshComparator() { return freshOrdering(); } @Generates private Ordering freshOrdering() { return new Ordering() { @Override public int compare(T left, T right) { return 0; } final String string = paramString(Ordering.class, freshInt()); @Override public String toString() { return string; } }; } @Generates static private Iterable freshIterable(E freshElement) { return freshList(freshElement); } @Generates static private Collection freshCollection(E freshElement) { return freshList(freshElement); } @Generates static private List freshList(E freshElement) { return freshArrayList(freshElement); } @Generates static private ArrayList freshArrayList(E freshElement) { ArrayList list = Lists.newArrayList(); list.add(freshElement); return list; } @Generates static private LinkedList freshLinkedList(E freshElement) { LinkedList list = Lists.newLinkedList(); list.add(freshElement); return list; } @Generates static private ImmutableList freshImmutableList(E freshElement) { return ImmutableList.of(freshElement); } @Generates static private ImmutableCollection freshImmutableCollection(E freshElement) { return freshImmutableList(freshElement); } @Generates static private Set freshSet(E freshElement) { return freshHashSet(freshElement); } @Generates static private HashSet freshHashSet(E freshElement) { return freshLinkedHashSet(freshElement); } @Generates static private LinkedHashSet freshLinkedHashSet(E freshElement) { LinkedHashSet set = Sets.newLinkedHashSet(); set.add(freshElement); return set; } @Generates static private ImmutableSet freshImmutableSet(E freshElement) { return ImmutableSet.of(freshElement); } @Generates static private > SortedSet freshSortedSet(E freshElement) { return freshTreeSet(freshElement); } @Generates static private > TreeSet freshTreeSet( E freshElement) { TreeSet set = Sets.newTreeSet(); set.add(freshElement); return set; } @Generates static private > ImmutableSortedSet freshImmutableSortedSet(E freshElement) { return ImmutableSortedSet.of(freshElement); } @Generates static private Multiset freshMultiset(E freshElement) { return freshHashMultiset(freshElement); } @Generates static private HashMultiset freshHashMultiset(E freshElement) { HashMultiset multiset = HashMultiset.create(); multiset.add(freshElement); return multiset; } @Generates static private LinkedHashMultiset freshLinkedHashMultiset(E freshElement) { LinkedHashMultiset multiset = LinkedHashMultiset.create(); multiset.add(freshElement); return multiset; } @Generates static private ImmutableMultiset freshImmutableMultiset(E freshElement) { return ImmutableMultiset.of(freshElement); } @Generates static private > SortedMultiset freshSortedMultiset( E freshElement) { return freshTreeMultiset(freshElement); } @Generates static private > TreeMultiset freshTreeMultiset( E freshElement) { TreeMultiset multiset = TreeMultiset.create(); multiset.add(freshElement); return multiset; } @Generates static private > ImmutableSortedMultiset freshImmutableSortedMultiset(E freshElement) { return ImmutableSortedMultiset.of(freshElement); } @Generates static private Map freshMap(K key, V value) { return freshHashdMap(key, value); } @Generates static private HashMap freshHashdMap(K key, V value) { return freshLinkedHashMap(key, value); } @Generates static private LinkedHashMap freshLinkedHashMap(K key, V value) { LinkedHashMap map = Maps.newLinkedHashMap(); map.put(key, value); return map; } @Generates static private ImmutableMap freshImmutableMap(K key, V value) { return ImmutableMap.of(key, value); } @Generates static private ConcurrentMap freshConcurrentMap(K key, V value) { ConcurrentMap map = Maps.newConcurrentMap(); map.put(key, value); return map; } @Generates static private , V> SortedMap freshSortedMap(K key, V value) { return freshTreeMap(key, value); } @Generates static private , V> TreeMap freshTreeMap( K key, V value) { TreeMap map = Maps.newTreeMap(); map.put(key, value); return map; } @Generates static private , V> ImmutableSortedMap freshImmutableSortedMap(K key, V value) { return ImmutableSortedMap.of(key, value); } @Generates static private Multimap freshMultimap(K key, V value) { return freshListMultimap(key, value); } @Generates static private ImmutableMultimap freshImmutableMultimap(K key, V value) { return ImmutableMultimap.of(key, value); } @Generates static private ListMultimap freshListMultimap(K key, V value) { return freshArrayListMultimap(key, value); } @Generates static private ArrayListMultimap freshArrayListMultimap(K key, V value) { ArrayListMultimap multimap = ArrayListMultimap.create(); multimap.put(key, value); return multimap; } @Generates static private ImmutableListMultimap freshImmutableListMultimap( K key, V value) { return ImmutableListMultimap.of(key, value); } @Generates static private SetMultimap freshSetMultimap(K key, V value) { return freshLinkedHashMultimap(key, value); } @Generates static private HashMultimap freshHashMultimap(K key, V value) { HashMultimap multimap = HashMultimap.create(); multimap.put(key, value); return multimap; } @Generates static private LinkedHashMultimap freshLinkedHashMultimap( K key, V value) { LinkedHashMultimap multimap = LinkedHashMultimap.create(); multimap.put(key, value); return multimap; } @Generates static private ImmutableSetMultimap freshImmutableSetMultimap( K key, V value) { return ImmutableSetMultimap.of(key, value); } @Generates static private BiMap freshBimap(K key, V value) { return freshHashBiMap(key, value); } @Generates static private HashBiMap freshHashBiMap(K key, V value) { HashBiMap bimap = HashBiMap.create(); bimap.put(key, value); return bimap; } @Generates static private ImmutableBiMap freshImmutableBimap( K key, V value) { return ImmutableBiMap.of(key, value); } @Generates static private Table freshTable(R row, C column, V value) { return freshHashBasedTable(row, column, value); } @Generates static private HashBasedTable freshHashBasedTable( R row, C column, V value) { HashBasedTable table = HashBasedTable.create(); table.put(row, column, value); return table; } @SuppressWarnings("rawtypes") // TreeBasedTable.create() is defined as such @Generates static private RowSortedTable freshRowSortedTable(R row, C column, V value) { return freshTreeBasedTable(row, column, value); } @SuppressWarnings("rawtypes") // TreeBasedTable.create() is defined as such @Generates static private TreeBasedTable freshTreeBasedTable(R row, C column, V value) { TreeBasedTable table = TreeBasedTable.create(); table.put(row, column, value); return table; } @Generates static private ImmutableTable freshImmutableTable( R row, C column, V value) { return ImmutableTable.of(row, column, value); } // common.reflect @Generates private TypeToken freshTypeToken() { return TypeToken.of(freshClass()); } // io types @Generates private File freshFile() { return new File(freshString()); } @Generates private static ByteArrayInputStream freshByteArrayInputStream() { return new ByteArrayInputStream(new byte[0]); } @Generates private static InputStream freshInputStream() { return freshByteArrayInputStream(); } @Generates private StringReader freshStringReader() { return new StringReader(freshString()); } @Generates private Reader freshReader() { return freshStringReader(); } @Generates private Readable freshReadable() { return freshReader(); } @Generates private Buffer freshBuffer() { return freshCharBuffer(); } @Generates private CharBuffer freshCharBuffer() { return CharBuffer.allocate(freshInt()); } @Generates private ByteBuffer freshByteBuffer() { return ByteBuffer.allocate(freshInt()); } @Generates private ShortBuffer freshShortBuffer() { return ShortBuffer.allocate(freshInt()); } @Generates private IntBuffer freshIntBuffer() { return IntBuffer.allocate(freshInt()); } @Generates private LongBuffer freshLongBuffer() { return LongBuffer.allocate(freshInt()); } @Generates private FloatBuffer freshFloatBuffer() { return FloatBuffer.allocate(freshInt()); } @Generates private DoubleBuffer freshDoubleBuffer() { return DoubleBuffer.allocate(freshInt()); } }